I'm working my way through some online examples and trying to get my head around how a HTML5 game may be put together.
I've got my head around the basics, such as the game loop, and separating the update and render logic.
I'm at the stage where I can generate a map from a tileset, and render a character who can walk around and have its sprite animate as it moves.
What I've struggling with is the way that the viewport follows the character as he moves. It's mostly working, the camera will follow - however, it has a very strange effect on the map that is generated.
The code so far is pretty long, but I've created a JSFiddle of the current way it works, and it illustrates the problems I'm having. The character can be moved with the arrow keys.
jsFiddle of the current issue
jsFiddle of the code
Incidentally, I've noticed if the character walks into the top left corner, it seems to be OK.
The code that is generating the map is:
tileSize = game.currentMap.tileset.tileWidth;
if (player.x >= (game.viewport.x / 2)) r = Math.floor(player.x - ((map.width * map.tileset.tileWidth) / 2))
for (r = Math.floor(game.viewport.x / map.tileset.tileWidth); r < Math.floor(game.viewport.x / map.tileset.tileWidth) + canvas.width / map.tileset.tileWidth + 1; r++) {
for (c = Math.floor(game.viewport.y / map.tileset.tileHeight); c < Math.floor(game.viewport.y / map.tileset.tileHeight) + canvas.height / map.tileset.tileHeight + 1; c++) {
var tile = ground[r][c];
var tileRow = (tile / game.currentMap.tileset.tilesInImgRow) | 0; // Bitwise OR operation
var tileCol = (tile % game.currentMap.tileset.tilesInImgRow) | 0;
ctx.drawImage(
game.currentMap.tileset.image, (tileCol * tileSize), (tileRow * tileSize),
tileSize,
tileSize, (c * tileSize) - game.viewport.x, (r * tileSize) - game.viewport.y,
tileSize,
tileSize);
tile = layer1[r][c];
tileRow = (tile / game.currentMap.tileset.tilesInImgRow) | 0;
tileCol = (tile % game.currentMap.tileset.tilesInImgRow) | 0;
ctx.drawImage(
game.currentMap.tileset.image, (tileCol * tileSize), (tileRow * tileSize),
tileSize,
tileSize, (c * tileSize) - game.viewport.x, (r * tileSize) - game.viewport.y,
tileSize,
tileSize);
}
}
However, as I'm studying the code from other resources - I'm a bit stuck as to whether the issue is being caused by this function or the function which generates and updates the viewport.
Any suggestions?
Update
The code that adjusts the viewport so that if the character is near the edge of the map, it no longer remains in the center of the viewport:
if (game.viewport.x <= 0) dX = player.x;
else if (game.viewport.x >= game.currentMap.tileset.tileWidth * game.currentMap.width - canvas.width) dX = player.x - game.viewport.x;
else dX = Math.round(canvas.width / 2 - player.width / 2);
if (game.viewport.y <= 0) dY = player.y;
else if (game.viewport.y >= game.currentMap.tileset.tileHeight * game.currentMap.height - canvas.height) dY = player.y - game.viewport.y;
else dY = Math.round(canvas.height / 2 - player.height / 2);
And the code that updates the viewport when the character is moved:
if (player.x + player.width / 2 < canvas.width / 2) {
game.viewport.x = 0;
} else if (player.x + canvas.width / 2 + player.width / 2 >= map.tileset.tileWidth * map.width) {
game.viewport.x = map.tileset.tileWidth * map.width - canvas.width;
} else {
game.viewport.x = Math.floor(player.x - (canvas.width / 2 - player.width / 2));
}
if (player.y + player.height / 2 < canvas.height / 2) {
game.viewport.y = 0;
} else if (player.y + canvas.height / 2 + player.height / 2 >= map.tileset.tileHeight * map.height) {
game.viewport.y = map.tileset.tileHeight * map.height - canvas.height;
} else {
game.viewport.y = Math.floor(player.y - (canvas.height / 2 - player.height / 2));
}
The viewport is initially set using the following:
var game = {
images: 0,
imagesLoaded: 0,
backgroundColor: '#000',
viewport: {
x: Math.floor(player.x - (canvas.width / 2 - playerSpriteSize / 2)),
y: Math.floor(player.y - (canvas.height / 2 - playerSpriteSize / 2))
},
currentMap: map,
fps: 0,
lastfps: 0,
fpsTimer: 0
}
Well, after playing with the code for a few days and many cups of coffee, I've managed to solve my own problem which I thought I would share for anyone else having similar issues.
The code to update the viewport was fine, the problem was the initial values assigned to the viewport and the way that the map x and y tiles were being iterated over.
In the game variable, I changed the viewport to be set to:
viewport: {
x: Math.floor(player.x - (canvas.width / 2)),
y: Math.floor(player.y - (canvas.height / 2))
},
And then in the drawMap function:
for (r = 0; r < map.rowTileCount; r++) {
for (c = 0; c < map.colTileCount; c++) {
This way, we are always generating the full map (which may be unnecessarily using extra resources, but we can always come back and look at this again later) and then we draw just a clipped part of this to the canvas which is exactly what I wanted.
http://jsfiddle.net/zdMSx/6/
Related
I know this has been asked a lot, but personally, i couldn't find a suitable answer. Pretty much everywhere, the function looks something like:
function rccol(rect, circle){
var dx = Math.abs(circle.x - (rect.x + rect.width / 2));
var dy = Math.abs(circle.y - (rect.y + rect.height / 2));
if(dx > circle.radius + rect.width / 2) return false;
if(dy > circle.radius + rect.height / 2) return false;
if(dx <= rect.width) return true;
if(dy <= rect.height) return true;
var dx = dx - rect.width;
var dy = dy - rect.height
return((dx * dx + dy * dy) <= (circle.radius * circle.radius));
}
And that works perfectly. The issue is that I'm using it for collision detection in a game, where the circle is the player's collision box, and the square is let's say a wall. I need to be able to determine where the contact occurred so that i can actually prevent the "player" from going into the "wall" so the simple boolean that gets returned, doesn't really work in my use-case.
Having circle center coordinates (cx, cy), you can calculate squared distance from circle center to rectangle.
dx = Max(Abs(cx - rect.center.x) - rect.width / 2, 0)
dy = Max(Abs(cy - rect.center.y) - rect.height / 2, 0)
SquaredDistance = dx * dx + dy * dy
When circle is outside of rectangle initially, it is possible to remove Max call.
dx = Abs(cx - rect.center.x) - rect.width / 2
dy = Abs(cy - rect.center.y) - rect.height / 2
Also we can remove Abs for known initial position (note - upto moment of sign change!)
if cx >= rect.center.x:
dx = cx - rect.center.x - rect.width / 2
else:
dx = - cx + rect.center.x - rect.width / 2
To avoid a lot od different cases, we can virtually put player in the first quadrant relative to rectangle center and correspondingly change coordinates and velocity components
if cx0 < 0:
cx0 = - cx0
vx = -vx
if cy0 < 0:
cy0 = - cy0
vy = -vy
To register collision moment, you have to substitute coordinates with parametric equations using start point and velocity vector components (vx, vy)
cx = cx0 + vx * t
cy = cy0 + vy * t
and solve (quadratic) equation for t
SquaredDistance - Radius^2 = 0
(cx0 + vx * t - rect.center.x - rect.width/2)^2 +
(cy0 + vy * t - rect.center.y - rect.height/2)^2 -
Radius^2 = 0
Ater that you can get zero (no collision), one (touch event) or two roots (moment of collision and moment of exit).
As in the title, im trying to create a grid of objects in P5 Spot(x, y, size), with a 4 pixel space between them and center it on the canvas without using translate, heres what i've got:
gridSize = 7;
spotSize = 60;
spots = [];
for (var y = height / 2 - ((gridSize * spotSize + gridSize * 4) / 2); y < (height / 2 - ((gridSize * spotSize + gridSize * 4) / 2)) + (gridSize * spotSize + gridSize * 4); y += spotSize + 4) {
for (var x = width / 2 - ((gridSize * spotSize + gridSize * 4) / 2); x < (width / 2 - ((gridSize * spotSize + gridSize * 4) / 2)) + (gridSize * spotSize + gridSize * 4); x += spotSize + 4) {
spots.push(new Spot(x, y, spotSize));
}
}
Problem is that my grid looks off, why is it not centered? Probably a really simple and stupid mistake but i cant find it. Any help appreciated.
My Spot object just draws an ellipse at the given x and y. Entire code at http://codepen.io/felipe_mare/pen/GWyMOL
-SOLVED-
spots.push(new Spot(x + spotSize/2, y + spotSize/2, spotSize));
Wasn't taking into account the fact that the ellipse is drawn from the center, so i have to add the radius of the circle spotSize/2
I am trying to create a simulation for experimenting with the Brownian motion. The idea is to make numerous particles (points) each moving randomly and colliding with each other, while conserving the total energy. That, I did succeed programming.
Now the problematic part is to make these point-like particles collide with a big circle and program the collision correctly. The difference is I made the particles move towards a totally random angle after their collision with each other, but when they collide with the circle, their previous path and speed, together with the circle's path and speed determines the result. So what I tried to do is to convert to a coordinate system attached to the center of the circle, calculate the collision point of every particle, which entered into the circle in the previous step, then calculate the angle in which they arrived. Now then the momentum of the particle is decomposed into two components: the tangential component remains the same, while the radial component changes according to the laws of collisions. This affects the motion of the circle as well. After calculating all the collisions, we move back to the original reference system, and draw all the objects.
The code can be found below, and it seems to be broken. I guess I was mistaken when calculating with the angles, because some particles can be shown arriving to the circle at one point, then "exiting" through the other side of it. I just lost track in following it all. I'm a complete newbie to programming, so be... um... gentle, please. :) Another strange thing is that the circle trends to move to a direction it started to move early, which is not what it supposed to do.
The mass of the particles are given, the mass of the circle is calculated with it's radius.
You can find the simulation on this website:
http://sixy.uw.hu/brown
It is not finished yet, but the start button works fine. :)
The part of the code I'm asking about is here: (shall I give you the whole code?)
function animateParticles() {
if ($('N').value != particleCount || $('S').value != circle.R) {
reset();
}
switch (activeGraph) {
case '#graph':
chartOptions.title.text = 'Távolság';
chartOptions.axisY.title = 'd [px]';
break;
if (Date.now() > lastAnim + 22) {
context.clearRect(0, 0, canvas.width, canvas.height);
// Particles step and collide
for (var i in particle) {
// Particle steps
{
particle[i].x += particle[i].vx;
particle[i].y += particle[i].vy;
}
// If not in the circle
if ((circle.x - particle[i].x) * (circle.x - particle[i].x) + (circle.y - particle[i].y) * (circle.y - particle[i].y) > circle.R * circle.R) {
// Collides with some of the rest of the particles
for (var j = 1; j <= $('N').value; j += Math.ceil(particleCount / (Math.random() * 50 + 50))) {
// If that's not in the circle as well
if ((circle.x - particle[j].x) * (circle.x - particle[j].x) + (circle.y - particle[j].y) * (circle.y - particle[j].y) > circle.R * circle.R) {
// If not himself
if (j != i) {
if (particle[i].coll == 0) {
// Collide
if (Math.pow((particle[i].x - particle[j].x), 2) + Math.pow((particle[i].y - particle[j].y), 2) <= 4) {
var tkpx = (particle[i].vx + particle[j].vx) / 2;
var tkpy = (particle[i].vy + particle[j].vy) / 2;
var fi = Math.random() * 2 * Math.PI;
var index;
var ix = particle[i].vx;
var iy = particle[i].vy;
particle[i].vx = Math.sqrt(Math.pow((ix - tkpx), 2) + Math.pow((iy - tkpy), 2)) * Math.cos(fi) + tkpx;
particle[i].vy = Math.sqrt(Math.pow((ix - tkpx), 2) + Math.pow((iy - tkpy), 2)) * Math.sin(fi) + tkpy;
particle[j].vx = -Math.sqrt(Math.pow((ix - tkpx), 2) + Math.pow((iy - tkpy), 2)) * Math.cos(fi) + tkpx;
particle[j].vy = -Math.sqrt(Math.pow((ix - tkpx), 2) + Math.pow((iy - tkpy), 2)) * Math.sin(fi) + tkpy;
}
}
}
if (particle[i].coll >= 1) {
particle[i].coll -= 1;
}
}
}
}
// Collision with the walls
{
if (particle[i].x >= canvas.width) {
var temp;
temp = particle[i].x - canvas.width;
particle[i].x = canvas.width - temp;
particle[i].vx *= -1
}
if (particle[i].x <= 0) {
var temp;
temp = -particle[i].x;
particle[i].x = temp;
particle[i].vx *= -1;
}
if (particle[i].y >= canvas.height) {
var temp;
temp = particle[i].y - canvas.height;
particle[i].y = canvas.height - temp;
particle[i].vy *= -1;
}
if (particle[i].y <= 0) {
var temp;
temp = -particle[i].y;
particle[i].y = temp;
particle[i].vy *= -1;
}
}
}
//console.log(circle);
// Circle steps and collides with particles
var cu = 0;
var cv = 0;
for (var i in particle) {
if ((circle.x - particle[i].x) * (circle.x - particle[i].x) + (circle.y - particle[i].y) * (circle.y - particle[i].y) < circle.R * circle.R) {
var pu;
var pv;
var px;
var py;
var px0;
var py0;
var t;
var t2;
var Tx;
var Ty;
var fi;
var p;
var q;
var cuu;
var cvv;
px = particle[i].x - circle.x;
py = particle[i].y - circle.y;
pu = particle[i].vx - circle.vx;
pv = particle[i].vy - circle.vy;
px0 = px - particle[i].vx;
py0 = py - particle[i].vy;
// Calculating the meeting point of the collision
t = (-(px0 * pu + py0 * pv) - Math.sqrt(circle.R * circle.R * (pu * pu + pv * pv) - Math.pow(px0 * pv - py0 * pu, 2))) / (pu * pu + pv * pv);
t2 = ((px - px0) * (px - px0) + (py - py0) * (py - py0)) / Math.sqrt(pu * pu + pv * pv) - t;
Tx = px0 + t * pu;
Ty = py0 + t * pv;
//console.log("TX: ", Tx);
//console.log("TY: ", Ty);
// Calculating the angle
{
if (Tx > 0 && Ty >= 0) {
fi = 2 * Math.PI - Math.atan(Ty / Tx);
}
else if (Tx < 0 && Ty >= 0) {
fi = Math.PI - Math.atan(Ty / Tx);
}
else if (Tx < 0 && Ty < 0) {
fi = Math.PI / 2 + Math.atan(Ty / Tx);
}
else if (Tx > 0 && Ty < 0) {
fi = -Math.atan(Ty / Tx);
}
else if (Tx = 0 && Ty >= 0) {
fi = 3 / 2 * Math.PI;
}
else if (Tx = 0 && Ty < 0) {
fi = Math.PI / 2;
}
}
//console.log("FI:", fi);
p = pu * Math.cos(fi) + pv * Math.sin(fi);
q = -pu * Math.sin(fi) + pv * Math.cos(fi);
cuu = 2 * q / (circle.M + 1);
cvv = 0;
q *= (1 - circle.M) / (1 + circle.M);
cu += cuu * Math.cos(-fi) + cvv * Math.sin(-fi);
cv += -cuu * Math.sin(-fi) + cvv * Math.cos(-fi);
//console.log("CU: ", cu);
//console.log("CV: ", cv);
pu = p * Math.cos(-fi) + q * Math.sin(-fi);
pv = -p * Math.sin(-fi) + q * Math.cos(-fi);
px = Tx + t2 * pu;
py = Ty + t2 * pv;
particle[i].x = px + circle.x;
particle[i].y = py + circle.y;
particle[i].vx = pu + circle.vx;
particle[i].vy = pv + circle.vy;
}
}
// Moving circle
{
circle.vx += cu;
circle.vy += cv;
circle.x += circle.vx;
circle.y += circle.vy;
for (var i in particle) {
while ((circle.x - particle[i].x) * (circle.x - particle[i].x) + (circle.y - particle[i].y) * (circle.y - particle[i].y) < circle.R * circle.R) {
particle[i].x += circle.vx / Math.abs(circle.vx);
particle[i].y += circle.vy / Math.abs(circle.vy);
}
}
circlePath.push({x: circle.x, y: circle.y});
if (Date.now() > lastDraw + 500) {
items.push({
y: Math.sqrt((circle.x - canvas.width / 2) * (circle.x - canvas.width / 2) + (circle.y - canvas.height / 2) * (circle.y - canvas.height / 2)),
x: (items.length + 1) / 2
});
drawChart(activeGraph);
lastDraw = Date.now();
}
}
//console.log(circle);
drawCircle();
for (var i in particle) drawParticle(particle[i]);
lastAnim = Date.now();
}
animation = window.requestAnimationFrame(animateParticles);
}
I'm currently trying to make a version of Breakout for university. Thanks for helping me to draw the paddle. I now find myself unable to cause the ball to bounce on the edge of the canvas - except in the middle. I've tried both adding and subtracting fractions to ball.x and ball.y (greater than or equal to canvas.width and canvas.height both work) but for less than or equal to 0, nothing seems to be successful. Here's the javascript code:
var canvas = document.getElementById("breakout");
var ctx = canvas.getContext("2d");
var PADDLE_WIDTH_PX = canvas.width / 5;
var PADDLE_HEIGHT_PX = 10;
var PADDLE_SPEED = 450;
var ball = {
x: canvas.width / 2, //pixels
y: canvas.height / 2, //pixels
xSpeed: 500, //pixels per second
ySpeed: 500, //pixels per second
radius: 100 //the ball is exceptionally large so that I can see what part of the ball is surpassing the canvas edge before the motion is reversed
}
var paddle = {
//radius: 5,
/*speed: 500,
TopRight: ctx.moveTo(canvas.width / 1.35, canvas.height - (canvas.height / 12.5)),
TopSide: ctx.lineTo(canvas.width / 2, canvas.height - (canvas.height / 12.5)),
RightSide: ctx.lineTo(canvas.width / 1.35, canvas.height - (canvas.height / 27.5)),
BottomLeft: ctx.moveTo(canvas.width / 2, canvas.height - (canvas.height / 27.5)),
LeftSide: ctx.lineTo(canvas.width / 2, canvas.height - (canvas.height / 12.5)),
BottomSide: ctx.lineTo(canvas.width / 1.35, canvas.height - (canvas.height / 27.5))*/
xSpeed: 450,
x: (canvas.width - PADDLE_WIDTH_PX) / 2,
y: canvas.height - PADDLE_HEIGHT_PX
}
var keysDown = {};
window.addEventListener("keydown",function(e) {
keysDown[e.keyCode] = true;
});
window.addEventListener("keyup",function(e) {
delete keysDown[e.keyCode];
});
function render() {
//clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height)
// draw the ball
ctx.fillStyle = "white";
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
ctx.closePath();
ctx.fill();
ctx.beginPath();
//ctx.fillStyle = "red";
/*ctx.moveTo(canvas.width - (2*paddle.x), canvas.height - (2*paddle.y));
/*ctx.lineTo(canvas.width / 2, canvas.height - (canvas.height / 12.5));
ctx.lineTo(canvas.width / 1.35, canvas.height - (canvas.height / 27.5));
ctx.moveTo(canvas.width / 2, canvas.height - (canvas.height / 27.5));
ctx.lineTo(canvas.width / 2, canvas.height - (canvas.height / 12.5));
ctx.lineTo(canvas.width / 1.35, canvas.height - (canvas.height / 27.5));
ctx.fill();
ctx.closePath();*/
/*ctx.lineTo(canvas.width - (2*paddle.x), canvas.height - paddle.y);
ctx.moveTo(canvas.width - paddle.x, canvas.height - paddle.y);
ctx.lineTo(canvas.width - paddle.x, canvas.height - (2*paddle.y));
ctx.lineTo(canvas.width - (2*paddle.x), (canvas.height -paddle.y));*/
ctx.fillRect(paddle.x, paddle.y, PADDLE_WIDTH_PX, PADDLE_HEIGHT_PX);
/*ctx.closePath();
ctx.fill();*/
}
function update(elapsed) {
//update the ball position according to the elapsed time
ball.y += ball.ySpeed * elapsed;
ball.x += ball.xSpeed * elapsed;
/*paddle.TopRight += paddle.speed * elapsed;
paddle.BottomLeft += paddle.speed * elapsed;
paddle.RightSide += paddle.speed * elapsed;
paddle.LeftSide += paddle.speed * elapsed;
paddle.TopSide += paddle.speed * elapsed;
paddle.BottomSide += paddle.speed * elapsed;*/
/*paddle.x += paddle.xSpeed * elapsed;
paddle.y += paddle.xSpeed * elapsed;*/
//bounce the ball of all edges
if (37 in keysDown && paddle.x > 0)
paddle.x -= PADDLE_SPEED * elapsed;
if (39 in keysDown && paddle.x + PADDLE_WIDTH_PX < canvas.width)
paddle.x += PADDLE_SPEED * elapsed;
if (ball.x+(ball.x/7) >= canvas.width) {
ball.x -= 5;
ball.xSpeed *= -1;
}
if (ball.x-(ball.x/7) <= 0) {
ball.x += 5;
ball.xSpeed *= -1;
}
if (ball.y+(ball.y/100) <= 0) {
ball.y += 5;
ball.ySpeed *= -1;
}
if (ball.y+(ball.y/3) >= canvas.height) {
ball.y -= 5;
ball.ySpeed *= -1;
}
/*
The problem here is that sometimes the ball gets 'stuck' to an edge.
This can occur when the ball passes beyond an edge in a frame when the
elapsed time is relatively large. In some cases, when the elapsed time in the
next frame is relatively short, the ball doesn't reach the edge to get back
into play. This results in another flip of the velocity and the ball becomes
'trapped' on the edge.
e.g.
xSpeed = -500, x = 10, elapsed = 0.2 => xSpeed = 500, x = -90 (xMovement = -100)
xSpeed = 500, x = -90, elapsed = 0.1 => xSpeed = -500, x = -40 (xMovement = +50)
xSpeed = -500, x = -40, elapsed = 0.1 => xSpeed = 500, x = -40 (xMovement = -50)
and so on ...until a larger elapsed time occurs in the right direction
The fix for this is to move the ball to the edge when the velocity is flipped.
*/
}
var previous;
function run(timestamp) {
if (!previous) previous = timestamp; //start with no elapsed time
var elapsed = (timestamp - previous) / 1000; //work out the elapsed time
update(elapsed); //update the game with the elapsed time
render(); //render the scene
previous = timestamp; //set the (globally defined) previous timestamp ready for next time
window.requestAnimationFrame(run); //ask browser to call this function again, when it's ready
}
//trigger the game loop
window.requestAnimationFrame(run);
Thanks for taking the time to read this
--ConfusedStudent
There is a number of issues with the paddle.
First of all you probably want it to be of a fixed size, so let's define
its dimensions at the beginning of the file (put it after the first two lines of your code because it uses the canvas to set the paddle width to 1/5th of its width - I think that's what you tried to do):
var PADDLE_WIDTH_PX = canvas.width / 5;
var PADDLE_HEIGHT_PX = 10;
With this you can initialize the paddle to be at the bottom of the canvas and in the middle:
var paddle = {
x: (canvas.width - PADDLE_WIDTH_PX) / 2,
y: canvas.height - PADDLE_HEIGHT_PX
}
x and y are the top-left corner of the paddle, so the right side is at x + PADDLE_WIDTH_PX and the bottom side is at y + PADDLE_HEIGHT_PX.
Knowing this, you can draw a path throught all the four corners like this:
ctx.beginPath();
ctx.moveTo(paddle.x, paddle.y);
ctx.lineTo(paddle.x + PADDLE_WIDTH_PX, paddle.y);
ctx.lineTo(paddle.x + PADDLE_WIDTH_PX, paddle.y + PADDLE_HEIGHT_PX);
ctx.lineTo(paddle.x, paddle.y + PADDLE_HEIGHT_PX);
ctx.lineTo(paddle.x, paddle.y);
ctx.closePath();
But since the paddle is just a rectangle, it's easier to use a method for drawing rectangles - fillRect, like this:
ctx.fillRect(paddle.x, paddle.y, PADDLE_WIDTH_PX, PADDLE_HEIGHT_PX);
Either way, all the four corners of the paddle move toggether so it doesn't grow or shrink.
So if you put it in your render function, it looks like this:
function render() {
//clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height)
// draw the ball
ctx.fillStyle = "white";
ctx.beginPath();
ctx.arc(ball.x, ball.y, ball.radius, 0, Math.PI * 2);
ctx.closePath();
ctx.fill();
// draw the paddle
ctx.fillStyle = "red";
ctx.fillRect(paddle.x, paddle.y, PADDLE_WIDTH_PX, PADDLE_HEIGHT_PX);
}
The last thing is to get the paddle to move when the left and right arrow keys are pressed.
The paddle only moves when either arrow left or arrow right is pressed. Otherwise its speed is 0 and it sits in its place. Therefore the paddle object doesn't need the xSpeed member variable.
Also, the paddle only moves horizontally so only its x variable changes, y is always the same.
Let's first define the speed of the paddle at the beginning of the file:
var PADDLE_SPEED = 300;
and then let's put the movement logic in the update function:
if (37 in keysDown && paddle.x > 0)
paddle.x -= PADDLE_SPEED * elapsed;
else if (39 in keysDown && paddle.x + PADDLE_WIDTH_PX < canvas.width)
paddle.x += PADDLE_SPEED * elapsed;
You can notice that the paddle position is only changed if an arrow key is pressed and the paddle is not at the edge.
All the other code that deals with the paddle should be removed from the update function so that it looks like this (I have removed most of the comments):
function update(elapsed) {
//update the ball position according to the elapsed time
ball.y += ball.ySpeed * elapsed;
ball.x += ball.xSpeed * elapsed;
if (37 in keysDown && paddle.x > 0)
paddle.x -= PADDLE_SPEED * elapsed;
else if (39 in keysDown && paddle.x + PADDLE_WIDTH_PX < canvas.width)
paddle.x += PADDLE_SPEED * elapsed;
//bounce the ball of all edges
if/*(*/(ball.x /*- (ball.x / 2))*/<= 0) {
ball.x = 1;
ball.xSpeed *= -1;
}
if /*(*/(ball.x /*+ (ball.x / 2))*/>= canvas.width) {
ball.x = ball.x -1;
ball.xSpeed *= -1;
}
if/*(*/(ball.y /*- (ball.y / 2))*/<= 0) {
ball.y = 1;
ball.ySpeed *= -1;
}
if /*(*/(ball.y /*+ (ball.y / 2))*/>= canvas.height) {
ball.y = ball.y -1;
ball.ySpeed *= -1;
}
}
I need som help on moving the elements in the array I created call the "Predator" array. Now I have all the images appearing on the SVG box as well as stored in the array.
I have difficulties calling them in a "for" loop and to move them randomly. Further that, I would want to animate the images more closely to real life. Example pls take a look on this website.
<script>
var startPredator = 20; //number of fish to start with
var Predator = []; //array of fish
var predatorW = 307; //fish width
var predatorH = 313; //fish height
var velocity = 100; //base velocity
var imageStrip; //predator image strip
//var backgroundImage; //background image
//var backgroundImageW = 981; //background image width
//var backgroundImageH = 767; //background image height
//var WIDTH = document.body.offsetWidth;
//var HEIGHT = document.body.offsetHeight;
function animate(){
document.animation.src = arraylist[imgNum].src
imgNum++
for (var i=arraylist[].length; i--;){
arraylist[i].move();
}
setInterval(function () { draw(); }, 16.7);
}
function start() {
for (var i = 0; i < arraylist.length; i++) {
var image = document.createElement("img");
image.alt = images[i][0];
image.width = "150";
image.height = "150";
image.src = images[i][1];
var j = i;
if (j > 2) j = i - 3;
document.getElementsByTagName("div")[j].appendChild(image);
}
}
function Predator() {
var angle = Math.PI * 2 * Math.random(); //set the x,y direction this predator swims
var xAngle = Math.cos(angle); //set the x value of the angle
var yAngle = Math.sin(angle); //set the y value of the angle
var zAngle = 1+-2*Math.round(Math.random()); //set if the predator is swimming toward us or away. 1 = toward us; -1 = away from us
var x = Math.floor(Math.random() * (WIDTH - predatorW) + predatorW / 2); //set the starting x location
var y = Math.floor(Math.random() * (HEIGHT - predatorH) + predatorH / 2); //set the starting y location
var zFar = 100; //set how far away can a predator go
var zFarFactor = 1; //set the max size the predator can be. 1=100%
var zClose = 0; //set how near a predator can come
var z = Math.floor(Math.random() * ((zFar - zClose))); //set the starting z location
var scale = .1; //set the rate of scaling each frame
var flip = 1; //set the direction of the fish. 1=right; -1=left
var cellCount = 16; //set the number of cells (columns) in the image strip animation
var cell = Math.floor(Math.random() * (cellCount-1)); //set the first cell (columns) of the image strip animation
var cellReverse = -1; //set which direction we go through the image strip
var species = Math.floor(Math.random() * 3); //set which species of predator this predator is. each species is a row in the image strip
// stop predator from swimming straight up or down
if (angle > Math.PI * 4 / 3 && angle < Math.PI * 5 / 3 || angle > Math.PI * 1 / 3 && angle < Math.PI * 2 / 3) {
angle = Math.PI * 1 / 3 * Math.random();
xAngle = Math.cos(angle);
yAngle = Math.sin(angle);
}
// face the predator the right way if angle is between 6 o'clock and 12 o'clock
if (angle > Math.PI / 2 && angle < Math.PI / 2 * 3) {
flip = -1;
}
function swim() {
// Calculate next position of species
var nextX = x + xAngle * velocity * fpsMeter.timeDeltaS;
var nextY = y + yAngle * velocity * fpsMeter.timeDeltaS;
var nextZ = z + zAngle * .1 * velocity * fpsMeter.timeDeltaS;
var nextScale = Math.abs(nextZ) / (zFar - zClose);
// If species is going to move off right side of screen
if (nextX + fishW / 2 * scale > WIDTH) {
// If angle is between 3 o'clock and 6 o'clock
if ((angle >= 0 && angle < Math.PI / 2)) {
angle = Math.PI - angle;
xAngle = Math.cos(angle);
yAngle = Math.sin(angle) * Math.random();
flip = -flip;
}
// If angle is between 12 o'clock and 3 o'clock
else if (angle > Math.PI / 2 * 3) {
angle = angle - (angle - Math.PI / 2 * 3) * 2
xAngle = Math.cos(angle);
yAngle = Math.sin(angle) * Math.random();
flip = -flip;
}
}
// If fish is going to move off left side of screen
if (nextX - fishW / 2 * scale < 0) {
// If angle is between 6 o'clock and 9 o'clock
if ((angle > Math.PI / 2 && angle < Math.PI)) {
angle = Math.PI - angle;
xAngle = Math.cos(angle);
yAngle = Math.sin(angle) * Math.random();
flip = -flip;
}
// If angle is between 9 o'clock and 12 o'clock
else if (angle > Math.PI && angle < Math.PI / 2 * 3) {
angle = angle + (Math.PI / 2 * 3 - angle) * 2
xAngle = Math.cos(angle);
yAngle = Math.sin(angle) * Math.random();
flip = -flip;
}
}
// If fish is going to move off bottom side of screen
if (nextY + fishH / 2 * scale > HEIGHT) {
// If angle is between 3 o'clock and 9 o'clock
if ((angle > 0 && angle < Math.PI)) {
angle = Math.PI * 2 - angle;
xAngle = Math.cos(angle);
yAngle = Math.sin(angle) * Math.random();
}
}
// If fish is going to move off top side of screen
if (nextY - fishH / 2 * scale < 0) {
// If angle is between 9 o'clock and 3 o'clock
if ((angle > Math.PI && angle < Math.PI * 2)) {
angle = angle - (angle - Math.PI) * 2;
xAngle = Math.cos(angle);
yAngle = Math.sin(angle);
}
}
// If fish is going too far (getting too small)
if (nextZ <= zClose && zAngle < 0) {
zAngle = -zAngle;
}
// If fish is getting to close (getting too large)
if (((WIDTH / fishW) * 10) < ((fishW * fish.length) / WIDTH)) {
zFarFactor = .3
}
else if (((WIDTH / fishW) * 2) < ((fishW * fish.length) / WIDTH)) {
zFarFactor = .5
}
else { zFarFactor = 1 }
if (nextZ >= zFar * zFarFactor && zAngle > 0) {
zAngle = -zAngle;
}
if (scale < .1) { scale = .1 }; //don't let fish get too tiny
//draw the fish
//locate the fish
ctx.save();
ctx.translate(x, y);
ctx.scale(scale, scale); // make the fish bigger or smaller depending on how far away it is.
ctx.transform(flip, 0, 0, 1, 0, 0); //make the fish face the way he's swimming.
ctx.drawImage(imageStrip, fishW * cell, fishH * species, fishW, fishH, -fishW / 2, -fishH / 2, fishW, fishH); //draw the fish
ctx.save();
scale = nextScale // increment scale for next time
ctx.restore();
ctx.restore();
//increment to next state
x = nextX;
y = nextY;
z = nextZ;
if (cell >= cellCount-1 || cell <= 0) { cellReverse = cellReverse * -1; } //go through each cell in the animation
cell = cell + 1 * cellReverse; //go back down once we hit the end of the animation
}
return {
swim: swim
}
}
</script>
My concern is more on the animate and start function. Am I calling the images from the array to move?
Please offer a helping hand. Thanks