So it has been a good long while since I programmed in a functional language. I have this code functioning normally; however I dislike it due to my OOD preferences.
var canvasWidth = 900;
var canvasHeight = 200;
var canvas0;
var context0;
var x0 = 20;
var y0 = 20;
var dx0 = 4;
var dy0 = 4;
function draw() {
context0.clearRect(0, 0, context0.canvas.width, context0.canvas.height);
context0.beginPath();
context0.fillStyle = "red";
context0.arc(x0, y0, 20, 0, 2 * Math.PI, true);
context0.closePath();
context0.fill();
// Boundary Logic
if (x0 < 13 || x0 > context0.canvas.width - 13) {
dx0 = (-dx0);
}
if (y0 < 13 || y0 > context0.canvas.height - 13) {
dy0 = (-dy0);
}
x0 += dx0;
y0 += dy0;
}
function init() {
'use strict';
canvas0 = document.getElementById("gfxCanvas");
context0 = canvas0.getContext('2d');
context0.canvas.width = canvasWidth;
context0.canvas.height = canvasHeight;
setInterval(draw, 10);
}
I have tried to refactor it into more object oriented design but I am having problems with the graphics processing. I can get the ball to appear once but I can not get it to move. Here is my refactored code; be aware that it is in a mid point of refactoring so there are some clear errors due to random tinkering.
function Ball(x, y, r, color) {
this.radius = r;
this.x = x;
this.y = y;
this.color = color;
console.log("x in creation" + this.x);
console.log("y in creation" + this.y);
draw();
}
Ball.prototype.draw = function(){
context1.beginPath();
console.log("x in DRAW()" + this.x);
console.log("y in DRAW()" + this.y);
context1.fillStyle = this.color;
context1.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, true);
context1.closePath();
context1.fill();
};
Ball.prototype.move = function(dx, dy){
// Boundary Logic
if (this.x < 13 || this.x > context1.canvas.width - 13) {
dx = (-dx);
}
if (this.y < 13 || this.y > context1.canvas.height - 13) {
dy = (-dy);
}
this.x += dx;
this.y += dy;
};
function initialize() {
canvas1 = document.getElementById("gfxCanvas2");
context1 = canvas1.getContext('2d');
context1.canvas.width = 900;
context1.canvas.height = 200;
ball1 = new Ball(20,20,20, "red");
setInterval(ball1.move(4,4), 10);
}
I would preferably like this method to be the movement method. The actual method would take the direction/speed vectors.
setInterval(ball1.move(4,4), 10);
setInterval(ball1.move(4,4), 10);
This doesn't work the way you intended it: It calls ball1.move(4,4) once, then calls the result of that every 10ms. You want the move method to be called every 10ms instead, right? There are two ways to do that:
setInterval(function() {
ball1.move(4,4);
}, 10);
or like this (more elegant in my opinion):
setInterval(ball1.move.bind(ball1,4,4), 10);
You can use bind:
setInterval(ball1.move.bind(ball1, 4, 4), 10);
That is equivalent of wrapping your call to move in an anonymous function:
setInterval(function() { ball1.move(4, 4); }, 10);
Then you will also need to update move so that it calls draw appropriately too.
In addition, I would not use a global variable to access the drawing context - even if I wasn't going to go completely OOP I would make sure that the draw method and the move method take a context (which, for the sake of simplicity could be "owned" by the ball).
Thanks for all the help, everyone. You clarified everything very well and pointed me in the correct direction. I suspected it was working in the manner you articulated however I wasn't entirely sure. I knew a couple of things where wrong with my implementation but couldn't put it so succinctly with my current knowledge.
However, I discovered my problem which your solutions were remedying in a more direct manner. I can't treat javascript with OOD paradigms. I will be refactoring the code using a more functional design pattern. Not attempting to coerce the code into a OO design will make things considerably easier. Your solutions helped but the bounds checking code was the next problem I ran into.
I'l be working this into a module design pattern for the ball objects which should be much more suited for js scope/closures and procedural workflow.
Related
I am working on a game in Javascript in which I have a player try to collect coins. Both are currently rects() with different dimensions, and I am trying to incorporate a function that alerts the user when they have gotten a coin. Currently, this is my code for my player & coin collision detection.
isTouching(player) {
return player.x + player.width > this.x &&
this.x + this.width > player.x &&
player.y + player.height > this.y &&
this.y + this.height > player.y;
}
However, when I loop through my coins array and check for collisions, nothing happens. Why is this? Here is my code pertaining to this:
for (let x = 0; x < 5; x ++) { coins[x].collisions(); }
and...
collisions() {
if (this.isTouching(player)) {
alert("you got a coin!");
}
}
Both my coins and player have their own class, with my coins being stored in an array.
let player;
let coins = [];
player = new Player();
for (let x = 0; x < 5; x ++) { coins.push(new Coin()); }
Even when the player touches the coin, there is no alert. How can I fix this?
P.S. I know there are many libraries capable of checking for collisions between rectangles, but I wanted to utilize my own function to check for collisions. Please let me know how/if I need to change my collision detection system.
Also, if my provided code is unclear, here is a .zip (download linked) containing my program code: https://www.mediafire.com/file/00rz1ta5s55rvzf/game.zip/file
EDIT: A comment suggested I use a library to check for collisions, which I'm technically not allowed to do, but for the sake of testing, I tried it. I imported bmoren's p5.collide2D library which worked for me in the past, and used this code (below). However, the issue still remains, and the collisions between objects is not detected at all.
New code utilizing library:
if (this.touched()) {
alert("you got a coin!");
}
touched() {
return collideRectRect(this.x, this.y, this.width, this.height, player.x, player.y, player.width, player.height);
}
Just read all of your code. I was able to get the coin alert working, here's what you need to change
in game.engine.js, change the function setup. Here I have updated your loop, problem is your random x and y of coins need to be passed to your coin class instance.
function setup() {
// Apply classes to empty variables
console.log("Creating player...");
player = new Player();
console.log("Creating world...");
world = new World();
console.log("Creating coins...");
for (let i = 0; i < number_of_coins; i++) { coins.push(new Coin(coin_cords_X[i], coin_cords_Y[i])); }
console.log("Creating controller...");
controller = new Controller();
// Draw canvas with set size
console.log("Creating game screen...");
createCanvas(1250, 750);
}
Now, your game.coin.js, needs to take the passed x and y in the constructor and use it instead.
class Coin {
// Setup player attributes
x;
y;
width;
height;
constructor(x, y) {
this.x = x;
this.y = y;
this.width = 30;
this.height = 30;
}
show(x) {
fill(player.color);
rect(this.x, this.y, this.width, this.height);
}
// rest of the methods will be as is
}
Having done both of these things, it should work fine.
I am attaching the modified program zip.
https://www.mediafire.com/file/4krl9e0trdxlcx3/game.zip/file
I'm in the middle of creating this simple animation using HTML5 Canvas and JavaScript and I'm experiencing a problem with flickering objects.
I was trying to find the solution on the internet before I asked this question and all I found was basically:
avoid loading new image , object at each new frame
use requestAnimationFrame()
I think I've done that all and the flickering is still happening.
(blue rectangles (obstacles) in my case.
The only solution that works is reducing the number of pixels in method responsible for moving the object, here:
obstacle.prototype.moveObstacle = function(){
this.x -=3
}
but the the animation is too slow.
Is there any way around it?
JSFiddle: https://jsfiddle.net/wojmjaq6/
Code:
var cnv = document.getElementById("gameField");
var ctx = cnv.getContext("2d");
var speedY = 1
var obst1 = new obstacle(cnv.width + 50);
var myBird = new bird(100, 1);
function bird(x, y) {
this.x = x;
this.y = y;
this.gravity = 0.3
this.gravitySpeed = 0
}
bird.prototype.drawbird = function() {
ctx.fillStyle = "red"
ctx.fillRect(this.x, this.y, 20, 20);
}
bird.prototype.animate = function() {
this.gravitySpeed += this.gravity
this.y += speedY + this.gravitySpeed
}
function obstacle(x) {
this.x = x;
this.y = 0;
this.obstLen = Math.floor(Math.random() * 400)
}
obstacle.prototype.drawobstacle = function() {
ctx.fillStyle = "blue";
ctx.fillRect(this.x, this.y, 15, this.obstLen)
ctx.fillRect(this.x, cnv.height, 15, -(cnv.height - this.obstLen - 100))
}
obstacle.prototype.moveObstacle = function() {
this.x -= 3
}
function myFun() {
ctx.clearRect(0, 0, cnv.width, cnv.height);
myBird.animate();
myBird.drawbird();
obst1.moveObstacle();
obst1.drawobstacle();
if (obst1.x < 0) {
obst1 = new obstacle(cnv.width + 50);
}
window.requestAnimationFrame(myFun)
};
function test() {
if (myBird.gravity > 0) {
myBird.gravity = -1
} else {
myBird.gravity = 0.3
}
}
document.getElementById("gameField").onmousedown = test
document.getElementById("gameField").onmouseup = test
window.requestAnimationFrame(myFun)
I do see some stuttering with the blue obstacle - the animation is not smooth.
Changing the x position of the obstacle based on the raw requestAnimationFrame loop will not necessarily result in a smooth operation as requestAnimationFrame just requests that the browser re-draws when it can.
The time between calls to requestAnimationFrame can vary depending on the power of the device the animation is on and how much there is to do each frame. There is no guarantee that requestAnimationFrame will give you 60 FPS.
The solutions are to decouple the changing of objects positions with the actual drawing of them, or factor it the elapsed time between frames and calculate the new position based on that to give a smooth animation.
Normally in my canvas animations I just use a library like GreenSock's Animation Platform (GSAP) https://greensock.com/get-started-js which can animate any numeric property over time, then I only have to write code for the drawing part.
It is possible to compute a time based animation in your own requestAnimationFrame, though there is a bit of complexity involved. This looks like a good tutorial on it http://www.javascriptkit.com/javatutors/requestanimationframe.shtml
Cheers,
DouG
Once again I go back to learning graphics from scratch (as many of the others when I was kid, I wanted to build games and stuff, but currenlty I am generic monkey-coder, but anyway).
Started to read this simple book (Building a 2D Game Physics Engine: Using HTML5 and JavaScript), and when it came to rectangle normals there were the picture and the equations related to it.
Here is the picture:
Here is the code, pretty striaghtforward, from the book (Vec2 is simple lib of functions which make simple vector operations)
function RigidShape(center) {
this.mCenter = center;
this.mAngle = 0;
gEngine.Core.mAllObjects.push(this);
}
var Rectangle = function(center, width,height){
RigidShape.call(this, center);
this.mType = "Rectangle";
this.mWidth = width;
this.mHeight = height;
this.mVertex =[];
this.mFaceNormal = [];
//0--TopLeft;1--TopRight;2--BottomRight;3--BottomLeft
this.mVertex[0] = new Vec2(center.x - width / 2, center.y -
height / 2);
this.mVertex[1] = new Vec2(center.x + width / 2, center.y -
height / 2);
this.mVertex[2] = new Vec2(center.x + width / 2, center.y +
height / 2);
this.mVertex[3] = new Vec2(center.x - width / 2, center.y +
height / 2);
//0--Top;1--Right;2--Bottom;3--Left
//mFaceNormal is normal of face toward outside of rectangle
this.mFaceNormal[0] = this.mVertex[1].subtract(this.mVertex[2]);
this.mFaceNormal[0] = this.mFaceNormal[0].normalize();
this.mFaceNormal[1] = this.mVertex[2].subtract(this.mVertex[3]);
this.mFaceNormal[1] = this.mFaceNormal[1].normalize();
this.mFaceNormal[2] = this.mVertex[3].subtract(this.mVertex[0]);
this.mFaceNormal[2] = this.mFaceNormal[2].normalize();
this.mFaceNormal[3] = this.mVertex[0].subtract(this.mVertex[1]);
this.mFaceNormal[3] = this.mFaceNormal[3].normalize();
var prototype = Object.create(RigidShape.prototype);
prototype.constructor = Rectangle;
Rectangle.prototype = prototype;
};
I was studying CS in university, including some linear algebra so I still rembemer that normal is the vector, perpendicular to surface it applied.
And the picture is fine.
But, when I try to make calculations due to whats in code I got the other picture in my mind(paper).
Here is what my mind drawns
The problem is that I can't see that the code from the book and the picture from the book are represents the same as it should be!
Is here is something wrong or did I missed something?
UPD normalize function
var Vec2 = function (x, y) {
this.x = x;
this.y = y;
};
Vec2.prototype.length = function () {
return Math.sqrt(this.x * this.x + this.y * this.y);
};
Vec2.prototype.normalize = function () {
var len = this.length();
if (len > 0) {
len = 1 / len;
}
return new Vec2(this.x * len, this.y * len);
};
in the book , the author explained why?
"Face normal index-0 is in the same direction as the vector from
vertex 2 to 1."
Hello and thank you for your help in advance.
I am trying to push/create a new "ring" every couple seconds. I have a ring with a couple variables for the X and Y. The problem I am encountering is, how do I get a new ring and also increment the variables? I need a new variable name for every ring?
Here is how far I have gotten so far:
http://codepen.io/hossman/pen/AfwkF
You can see in the demo how 1 ring goes out, but I want more than 1 ring to go out of my eyes. So for instance 1 ring goes and then it waits a second and then shoots out another ring, so now there are 2 rings on the canvas, then 3, then 4, etc.... I have thought of multiple ways like using arrays and setTimeouts, but I cant put my finger on it. The only other idea I have is to create multiple variables with different names and have each ring be incremented, but thats not very D.R.Y.
Anyhelp?
Please ask questions if I didn't explain it good enough. Thanks again!
Add this to your global vars at the top (and set to whatever you want the distance to be between circles):
var distanceApart = 40;
Then update your main loop like this:
requestAnimationFrame(function print() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
var leftRing = new Ring(x, y);
var rightRing = new Ring(x2, y2);
var temp = startRadius;
var temp2 = 0;
while(temp > 0){
leftRing.draw(ctx, startRadius - temp2 , 'red');
rightRing.draw(ctx, startRadius - temp2 , 'red');
temp2 = temp2 + distanceApart;
temp = temp - distanceApart;
}
startRadius += increase;
requestAnimationFrame(print);
});
Forked here: http://codepen.io/anon/pen/plBmj
(Looks very memorizing!)
I would rewrite parts of your code to enable this. For example I would rewrite your Ring class as follows:
var Ring = defclass({
constructor: function (x, y, r) {
this.x = x;
this.y = y;
this.r = r;
},
draw: function (context) {
context.beginPath();
context.arc(this.x, this.y, this.r, 0, Math.PI * 2);
context.stroke();
return this;
},
addRadius: function (r) {
return new Ring(this.x, this.y, this.r + r);
}
});
Your Ring class constructor now takes x, y and a radius r. The addRadius function returns a new Ring instead of mutating the original one. This is good because immutability makes your code easier to work with. Oh, and defclass is declared as:
function defclass(prototype) {
var constructor = prototype.constructor;
constructor.prototype = prototype;
return constructor;
}
Then we create the two rings for your eyes:
var radius = 10;
var delta = 0.1;
var left = new Ring(cx - (cx / 3.6), cy - 5, radius);
var right = new Ring(cx + (cx / 3.6), cy - 10, radius);
After that we call the animation loop:
var interval = 50 / 3;
var start = Date.now();
loop(start, [left, right]);
Since we want to playback at 60 FPS the interval is 1000 / 60 which can be simplified to 50 / 3. The animation loop is defined as follows:
function loop(last, rings) {
var next = last + interval;
context.clearRect(0, 0, width, height);
var newRings = rings.map(function (ring) {
return ring.draw(context).addRadius(delta);
});
var now = Date.now();
setTimeout(loop, next - now, next,
Math.floor((now - start) / 1000) === rings.length / 2 ?
[left, right].concat(newRings) : newRings);
}
Here's what's happening:
First we clear the screen.
Then we draw all the rings and increase their size.
If one second has elapsed we add two new rings to the array.
Finally we calculate when to call loop again so that it fires after the correct interval.
See the demo: http://jsfiddle.net/LAr76/
I am attempting to create a small asteroids game in Javascript, and I've gotten it to the point where the ship draws on the screen and can fly around. However, whenever I try to rotate it by a certain amount, it can only rotate between +/- PI/2. I need it to cover more than just that range of 180 degrees, or else the ship can never turn around. I'm trying to do this from scratch using a custom 2D Vector class, and I've had a couple people look at it with no luck as to what to do.
Here's my vector code, or at least the constructor and rotation functions.
function Vec2D(x, y) {
var self = this;
var sqrt = Math.sqrt;
this.x = x !== null ? Number(x) : 0;
this.y = y !== null ? Number(y) : 0;
}
Vec2D.prototype.rotate = function (deg) {
var theta = deg * (Math.PI / 180),
xTemp = this.x;
this.x = this.x * Math.cos(theta) - this.y * Math.sin(theta);
this.y = xTemp * Math.sin(theta) + this.y * Math.cos(theta);
return this;
}
And here's the code for where my ship is trying to rotate.
function Ship(x_, y_, size_) {
this.position = new Vec2D(x_, y_);
this.velocity = new Vec2D(0, 0);
this.forward = new Vec2D(0, 0);
//some other things
this.turningRight = false;
this.turningLeft = false;
this.turnAmt = 5;
//some more things
}
Ship.prototype.update = function () {
//other update code
if (this.turningRight) {
this.forward.rotate(this.turnAmt);
console.log("right");
}
if (this.turningLeft) {
this.forward.rotate(-1.0 * this.turnAmt);
console.log("left");
}
//end of rotation code in update
}
I can reproduce more code if necessary, but this is all the relevant code as far as I can tell. I've tried console printing, I've tried messing with the rotation matrix, and I've even tried using only radians as opposed to converting it from degrees every time (which in all honesty I really should be doing anyway).
Any thoughts on my gross novice JavaScript?