Spel: PONG

bewegende bal Botsing bal met ‘muur’ Richting bal berekenen

Pong: bewegende bal

In het vorige onderdeel heb je het uiterlijk van het spel gemaakt. Als je dat eenmaal af hebt wil je natuurlijk dat je kan spelen. Dit wordt uitgelegd in een aantal paragrafen waarbij we beginnen met het bewegen van de bal. Zoals je in het vorige hoofdstuk met het voorbeeld van het bewegen van de vormen hebt gezien komt beweging er eigenlijk op neer dat alles steeds opnieuw getekend wordt maar dan op nieuwe posities. Omdat dat zo snel gebeurt lijkt het voor ons mensen alsof het een vloeiende beweging is.

Bij de eerste stap hebben we de bal getekend, nu moeten we zorgen dat daar beweging in komt. We hebben de bal getekend door de volgende functie aan te roepen: ellipse(400, 300, 30, 30). Willen we hem nu laten bewegen moeten we zorgen dat we de x- en y-coördinaat variabel maken en niet van te voren op een vast punt zetten. Dit doen we weer door twee (globale) variabelen te definiëren: ballX en ballY. In eerste instantie tekenen we de bal op positie (400,300) dus zetten we in de setup() functie de volgende regels: ‘ballX = 400;’ en ‘ballY = 300;’. Daarna moet je de bal telkens een stukje verder tekenen dus moet je de variabele enigszins aanpassen. Dit doen we door de volgende regels code: ‘ballX += 5;’ en ‘ballY += 5;’. De code met bewegende bal ziet er dan nu zo uit:

//globale variabele
let ballX = 0; //x-coordinate of the ball
let ballY = 0; //y-coordinate of the ball

// setup() function, everything to setup the game
function setup() {
	// maak een tekenblad (canvas)
	let myCanvas = createCanvas(800, 400); // maak deze 800 (breedte) bij 600 (hoogte)
	myCanvas.parent("pongcontainer"); // plaats deze in de div
	ballX = 400; //set the x-coordinate for the start position of the ball
	ballY = 300; //set the y-coordinate for the start position of the ball
} 

// draw() function (repeatedly called), where to draw what
function draw() {
	background(0, 0, 0); //make background black

	fill(255,255,255); //from this line onward draw everything in white
	rect(20, 280, 20, 40); //draw left bat
	rect(760, 280, 20, 40); //draw right bat
	ellipse(ballX, ballY, 30, 30); //draw ball
	ballX += 5; //move the ball 5 pixels in the x-direction
	ballY += 5; //move the ball 5 pixels in the y-direction

	textSize(30); //choose the size of the text
	text("Points: 0", 100 ,30); //draw the score for the left player
	text("Points: 0", 600, 30); //draw the score for the right player
              //(600 = 800 – 200 not the 600 from the size of the window)
}

Als je deze code draait zal je zien dat de bal uit het scherm verdwijnt. Dit willen we natuurlijk niet laten gebeuren. We willen dat de bal van de zijkanten ‘stuitert’. Niet alle zijkanten moet de bal af stuiteren. Alleen de boven en onder zijkant, de linker en rechter zijkant moet de bal wel ‘doorheen’ kunnen (dan is er door iemand een punt gescoord). Voor het ‘stuiteren’ van een object tegen de rand heeft processing jammer genoeg geen functie gemaakt. Dit betekent dat we zelf moeten controleren of de bal de zijkant bereikt heeft of niet.

Pong: Botsing bal met ‘muur’

We hebben in de code de variabele ballX en ballY gekozen als de x- en y-coördinaat voor de bal. Elke keer dat de functie draw() weer uitgevoerd wordt, worden de waarden met 5 opgehoogd (regel 20 en 21). Dit zorgt ervoor dat het lijkt alsof de bal beweegt (feitelijk wordt de bal steeds opnieuw getekend). Met behulp van deze coördinaten kunnen ook kijken of de bal tegen een ‘muur’ is gebotst. We hoeven alleen de boven en onderkant te doen op dit moment. Zoals je ondertussen weet wordt de verticale richting van het ‘scherm’ waarop getekend wordt weergegeven met de y-as. Voor de botsing met de boven- en onderkant betekend het dat er gekeken moet worden of de y-coördinaat van de bal de bovenkant of de onderkant bereikt (de grootte van het scherm is 800x600, dus ballY moet altijd groter dan 0 zijn en kleiner dan 600).

//globale variabele
let ballX = 0; //x-coordinate of the ball
let ballY = 0; //y-coordinate of the ball

// setup() function, everything to setup the game
function setup() {
	// maak een tekenblad (canvas)
	let myCanvas=createCanvas(800, 600); // maak deze 800 (breedte) bij 600 (hoogte)
	myCanvas.parent("pongcontainer"); // plaats deze in de div
	ballX = 400; //set the x-coordinate for the start position of the ball
	ballY = 300; //set the y-coordinate for the start position of the ball
} 

// draw() function (repeatedly called), where to draw what
function draw() {
	background(0, 0, 0); //make background black

	fill(255,255,255); //from this line onward draw everything in white
	rect(20, 280, 20, 40); //draw left bat
	rect(760, 280, 20, 40); //draw right bat
	ellipse(ballX, ballY, 30, 30); //draw ball
	if(ballY > 0 && ballY < 600) //if ball is in canvas
	{
		ballY += 5; //move the ball 5 pixels down in the y-direction
	}
	else if(ballY > 600) //if ball is ‘under’ the lower ‘wall’
	{
		ballY -= 5; //move the ball 5 pixels up in the y-direction
	}
	else if(ballY < 0) //if ball is ‘under’ the lower ‘wall’
	{
		ballY += 5; //move the ball 5 pixels down in the y-direction
	}
	ballX += 5; //move the ball 5 pixels in the x-direction

	textSize(30); //choose the size of the text
	text("Points: 0", 100 ,30); //draw the score for the left player
	text("Points: 0", 600, 30); //draw the score for the right player
	          //(600 = 800 – 200 not the 600 from the size of the window)
}

Als je deze code draait zie je dat de bal binnen het scherm blijft (boven en onder). Wat er nu gebeurt is dat de bal op de onderkant blijft ‘plakken’ (doe je bij regel 20 ‘-=’ ipv ‘+=’ gebeurt hetzelfde maar dan aan de bovenkant). Hoe dat komt... zodra de bal erbuiten valt worden er 5 pixels vanaf getrokken maar dan valt hij weer binnen het scherm en zullen er weer 5 bij opgeteld worden zodat de bal er weer buiten valt, zo blijf je oneindig in deze loop van +5 en -5.

Zodra de bal dus buiten het scherm valt wil je de richting omdraaien i.p.v. dat je ballY van +5 naar -5 doet. Hiervoor hebben we een beetje (simpele) wiskunde nodig.

Pong:Richting van de bal berekenen

We hebben er hierboven voor gekozen om de bal te laten bewegen door de x- en y-coördinaat met een vast getal aan te passen. Zoals we gezien hebben kunnen we dat beter doen door een richting te kiezen (zodat we die bij ‘botsingen’ eenvoudig kunnen veranderen). Om richtingen te berekenen kunnen we de hoeken van een rechthoekige driehoek (goniometrie) gebruiken. Hiervoor hebben we een eerste richting nodig. Deze kan je willekeurig laten kiezen (zie regel 17 en 19 hieronder). Dan bereken je de hoek waarin de bal beweegt aan de hand van deze directionX en directionY (zie regel 30). En als laatst bereken je aan de hand van de snelheid (die je van tevoren bepaald, regel 4) hoeveel pixels er in de y-richting (regel 31) en hoeveel pixels in de x-richting (regel 32) veranderen.

//globale variabele
let ballX = 0; //x-coordinate of the ball
let ballY = 0; //y-coordinate of the ball
let ballSpeed = 10; //speed of the ball
let directionX = 0; //x direction of the ball
let directionY = 0; //y direction of the ball
let alpha = 0; //the corner of the triangle made from the directionX and directionY variable
let deltaX = 0; //the amount of pixels in x direction moved per step
let deltaY = 0; //the amount of pixels in y direction moved per step


// setup() function, everything to setup the game
function setup() {
	// maak een tekenblad (canvas)
	let myCanvas=createCanvas(800, 600); // maak deze 800 (breedte) bij 600 (hoogte)
	myCanvas.parent("pongcontainer"); // plaats deze in de div
	ballX = 400; //set the x-coordinate for the start position of the ball
	ballY = 300; //set the y-coordinate for the start position of the ball
	directionX = Math.random()*2-1; //Math.random() gets number between 0 and 1, by
        //multiplying with 2 and subtracting 1 it becomes a number between -1 and 1
	directionY = Math.random()*2-1;
} 

// draw() function (repeatedly called), where to draw what
function draw() {
	background(0, 0, 0); //make background black

	fill(255,255,255); //from this line onward draw everything in white
	rect(20, 280, 20, 40); //draw left bat
	rect(760, 280, 20, 40); //draw right bat
	ellipse(ballX, ballY, 30, 30); //draw ball
	alpha = Math.atan(directionY/directionX); //calculate direction
	deltaY = ballSpeed*Math.sin(alpha); //change in y-coordinate according to direction
	deltaX = ballSpeed*Math.cos(alpha); //change in x-coordinate according to direction
	if(directionX < 0) //if direction is negative (ball moves down left)
	{
		ballX -= deltaX; //x-coordinate has to decrease
	}
	else //direction is positive (ball moves right)
	{
		ballX += deltaX; //x-coordinate has to increase
	}
	ballY += deltaY; //increase the y-coordinate (deltaY can be negative so ball goes
                     //both up and down)
	textSize(30); //choose the size of the text
	text("Points: 0", 100 ,30); //draw the score for the left player
	text("Points: 0", 600, 30); //draw the score for the right player
              //(600 = 800 – 200 not the 600 from the size of the window)
}

Als je de code nu een aantal keer draait zie je dat de bal verschillende richtingen op gaat. Alleen ‘botst’ nog niet tegen de boven- en onderkant. Nu moeten we eigenlijk hetzelfde als net doen alleen dan kan je de richting veranderen door ‘directionY *= -1;’ ipv ballX van +5 naar -5 (of andersom) doen. De code ziet er dan zo uit:

//globale variabele
let ball_radius = 30; //radius of the ball
let ballX = 0; //x-coordinate of the ball
let ballY = 0; //y-coordinate of the ball
let ballSpeed = 10; //speed of the ball
let directionX = 0; //x direction of the ball
let directionY = 0; //y direction of the ball
let alpha = 0; //the corner of the triangle made from the directionX and directionY variable
let deltaX = 0; //the amount of pixels in x direction moved per step
let deltaY = 0; //the amount of pixels in y direction moved per step


// setup() function, everything to setup the game
function setup() {
	// maak een tekenblad (canvas)
	let myCanvas=createCanvas(800, 600); // maak deze 800 (breedte) bij 600 (hoogte)
	myCanvas.parent("pongcontainer"); // plaats deze in de div
	ballX = 400; //set the x-coordinate for the start position of the ball
	ballY = 300; //set the y-coordinate for the start position of the ball
	directionX = Math.random()*2-1; //Math.random() gets number between 0 and 1, by
        //multiplying with 2 and subtracting 1 it becomes a number between -1 and 1
	directionY = Math.random()*2-1;
} 

// draw() function (repeatedly called), where to draw what
function draw() {
	background(0, 0, 0); //make background black

	fill(255,255,255); //from this line onward draw everything in white
	rect(20, 280, 20, 40); //draw left bat
	rect(760, 280, 20, 40); //draw right bat
	ellipse(ballX, ballY, ball_radius, ball_radius); //draw ball
	alpha = Math.atan(directionY/directionX); //calculate direction
	deltaY = ballSpeed*Math.sin(alpha); //change in y-coordinate according to direction
	deltaX = ballSpeed*Math.cos(alpha); //change in x-coordinate according to direction
	if(directionX < 0) //if direction is negative (ball moves down left)
	{
		ballX -= deltaX; //x-coordinate has to decrease
	}
	else //direction is positive (ball moves right)
	{
		ballX += deltaX; //x-coordinate has to increase
	}
	ballY += deltaY; //increase the y-coordinate (deltaY can be negative so ball goes
                     //both up and down)
	//collision detection
	//ball collides with top and bottom of screen
	if(ballY - (0.5 * ball_radius) < 0) //top: center of ball minus half radius < 0
		directionY *= -1;
	if(ballY + (0.5 * ball_radius) > 600) //bottom: center of ball plus half radius > 600
		directionY *= -1;
	textSize(30); //choose the size of the text
	text("Points: 0", 100 ,30); //draw the score for the left player
	text("Points: 0", 600, 30); //draw the score for the right player
              //(600 = 800 – 200 not the 600 from the size of the window)
}

Als je deze code nu draait zie je dat de bal aan het begin alle mogelijke richtingen op gaat en als het de boven- en onderkant raakt de richting veranderd wordt. In de code hierboven is ervoor gekozen om van tevoren vast te leggen hoe groot de bal is (regel 2). Dit heeft als voordeel dat als je de bal wat groter of kleiner wilt maken je het maar op een plek hoeft aan te passen. Bij de berekening of de bal botst met de muren is er ook rekening gehouden met de straal van de bal (regel 44: als de y-coördinaat min halve radius kleiner is dan 0 dan valt de bal buiten het veld en dus moet de richting veranderd worden. Regel 46 doet hetzelfde maar dan aan de onderkant).

Opdrachten:

  1. Zorg dat de bal ook van richting veranderd als een van de ‘bats’ geraakt wordt.
    Tip: controleer de x-coördinaat van de bal (ballX) met de x-coördinaat van het binnenste gedeelte van de bat (ballX + (0.5 * ball_radius) > bat2X). Zorg ook dat de y-coördinaat van de bal binnen de y-coördinaat van de bat valt (ballY > bat2Y && ballY < (bat2Y + bat_height)). Zoals je ziet is ervoor gekozen om de bat_height en bat_width van tevoren te kiezen en te definiëren (net zoals de ball_radius in het voorbeeld hierboven).