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
Related
I am making a simple game in HTML Canvas. As a part of it, in the background i want to make falling stars which create the illusion of travelling. After the star reaches the end of the canvas i want to remove it. Each star is an instance of Star class, and depending on it's radius it has a different velocity. This is where problems start. When using constant velocity for every star, they disappear one by one like they should be. When velocity is changed, stars that "overtake" slower stars, when reached the end of the canvas, do not only dissapear themselves but also remove every star that was in array before them.
I have tried many solutions described below:
let canvas = document.querySelector("canvas");
let c = canvas.getContext('2d');
Star Class declaration:
class Star{
constructor(x, y, radius, color, velocity){
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
this.velocity = velocity
}
draw(){
c.globalCompositeOperation='destination-over'
c.beginPath()
c.arc(this.x, this.y, this.radius, 0, Math.PI*2, false);
c.fillStyle = this.color;
c.shadowColor= "white"
c.shadowBlur=12
c.fill();
c.shadowBlur=0
}
update(){
this.draw();
this.y = this.y + this.radius/2;
}
}
Creating stars and adding it to array
let stars = [];
function createStar(){
setInterval(()=>{
//Create random radius and X position
let randomRadius = Math.floor((Math.random()*5))
let randomXPosition = Math.floor((Math.random()*780)+15)
let velocity = 1;
stars.push(new Star(randomXPosition, -randomRadius, randomRadius, "white",velocity));
console.log("stars:"+ stars.length);
},300)
}
Below here I use a function calling itself to clear and refresh the star drawing. I have tried looping through stars array with forEach method, reversed loop (like in example below), I tried putting the if statement with splice function in seperate setTimeout(()=>{},0) or setTimeout(()=>{},10). I tried using the condition like
(forEach method removes stars, however the number of active stars do not remain more or less the same. It constantly slowly increases)
function animate(){
c.clearRect(0, 0, canvas.width, canvas.height);
for(let i = stars.length-1; i>=0; i--){
stars[i].update()
if (stars[i].y > canvas.height +stars[i].radius ){
stars.splice(stars[i], 1)
}
}
requestAnimationFrame(animate);
}
animate();
createStar();
I tried using the condition like:
if (stars[i].y > 5000 ){
stars.splice(stars[i], 1)
}
But it's not how it's supposed to be solved, because stars live for 5000 pixels longer,what makes game laggy.
Just to be clear on the problem.
If i generate 5 stars every 300ms and push the into the array called "stars" I get e.g.
[star1, star2, star3, star4, star5]
Lets say star1 and star 2 have small radius and they move slowly. Star3 is bigger therefore it moves faster and overtakes first two. When star3 reaches canvas.height + star[i].radius it disappear just over the canvas, but it also makes every star in array that was before star3 disappear (in this case it's star1 and star2).
This is my first post on stackoverflow. I apologise for all understatements in advance.
HTML Canvas
<body>
<div class="container">
<button class="logOutButton">Log Out</button>
<button class="topScores">Top 10</button>
<header>
<p class="hello">Hello <span id="spanName"></span></p>
<p class="topScore">Your TOP score: <span id="score"></span></p>
</header>
<div class="gameTitle">
<h2 class="title">Space Warrior</h2>
</div>
<canvas class="canvas" width="800" height="500"></canvas>
</div>
<script src="User.js"></script>
</body>
EDIT
I changed stars.splice(stars[i], 1) to stars.splice(i, 1) - not working
I tried adding another removal array like below but array stars just slowly gets bigger (even though some elements get removed)
var removeStar = [];
// stars.forEach((star,starIndex)=>{
let total = stars.length-1;
for(let i = total; i>=0; i--){
stars[i].update();
if (stars[i].y > canvas.height + stars[i].radius){
removeStar.push(i);
}
};
for(let i = removeStar.length; i>0; i--){
stars.splice(removeStar[i-1],1)
}
you wrote:
stars.splice(stars[i], 1)
first argument should be index which you want to remove ...just index stars.splice(i, 1)
..another problem is that you changing the array while looping within it, which is bad idea.
see this answer to very similar question:
https://stackoverflow.com/a/65725703/3054380
After fixing the bug mentioned above and adding minVelocity and star limiting condition (because you adding with interval and removing when star goes off the canvas - we need to limit in case interval goes faster) ...now everything looks good - working snippet below (added maxStars minVelocity)
let canvas = document.querySelector(".mycanvas");
let c = canvas.getContext('2d');
canvas.width = 300;
canvas.height = 150;
let maxStars = 60;
let minVelocity = 0.5;
let stars = [];
class Star {
constructor(x, y, radius, color, velocity) {
this.x = x;
this.y = y;
this.radius = radius;
this.color = color;
this.velocity = velocity
}
draw() {
c.globalCompositeOperation = 'destination-over'
c.beginPath()
c.arc(this.x, this.y, this.radius, 0, Math.PI * 2, false);
c.fillStyle = this.color;
c.shadowColor = "white"
c.shadowBlur = 12
c.fill();
c.shadowBlur = 0
}
update() {
this.draw();
this.y = this.y + Math.max(minVelocity, this.velocity * this.radius / 2);
}
}
function createStar() {
if (stars.length < maxStars) {
//Create random radius and X position
let randomRadius = Math.floor((Math.random() * 5));
let randomXPosition = Math.floor((Math.random() * (canvas.width - 20)) + 10);
let velocity = 1;
stars.push(new Star(randomXPosition, -randomRadius, randomRadius, "white", velocity));
console.log("stars:" + stars.length);
}
}
function animate() {
c.clearRect(0, 0, canvas.width, canvas.height);
for (let i = stars.length - 1; i >= 0; i--) {
stars[i].update()
if (stars[i].y > canvas.height) {
stars.splice(i, 1)
}
}
requestAnimationFrame(animate);
}
setInterval(createStar, 50);
animate();
.mycanvas {
width: 300px;
height: 150px;
border: 1px solid #f00;
}
<body style="background-color:#000;color:#fff;">
<div class="container">
<canvas class="mycanvas"></canvas>
</div>
<script src="User.js"></script>
</body>
I think I found the issue causing your everexpanding array. It has nothing to do with your splice function, which is implemented incorrectly as webdev-dan mentioned in his answer.
follow this logic
The radius has been set with Math.floor((Math.random()*5)).This means the radius can be 0.
In your update method you are increasing y based on the radius in this.y = this.y + this.radius/2. So this is possibly changing y with 0.
In if (stars[i].y > 5000 ) you are removing values of y over 5000.
So if the radius is 0, y doesn't change, stars are never removed. Nor can you see them visually.
solution
You could guarantee a minimal speed of 1. this.y += Math.max(1, this.radius/2).
PS: I had to do quite a bit of refactoring to figure this out. You are writing your code in a too complex way. Several types of logic are mixed up.
You really want to separate out your rendering logic from management of the stars object.
It is also quite hard to mentally keep track of the stars array because you are modifying it from anywhere; inside for loops, with async code ( the interval ).
This is what I ended up while trying to clean up your code a bit: https://jsfiddle.net/5hk0vscg/1/ Hope it's useful. Note that it is not a full cleanup, but it's an improvement over your current code.
I'm learning to use Processing.JS have have a very basic idea on what I'm doing. I'm learning to create objects using cases. The aim of this code is to create random objects (balls) that bounce around the screen and when the mouse is clicked a new ball is created, leaving the original and the new created. I want to be able to do this multiple times, with all of the balls randomly bouncing around. I have the code working for one ball and have the code to a point where I'm creating new objects and they are being added to an array, however every time I click the single ball just gets reset to the middle of the screen. This is the code:
//random ball maker
Ball[] newBall = new Ball[1];
void setup(){
size(500,500);
newBall[0] = new Ball();
}
void draw(){
//newBall.display();
// newBall.movement();
for (int i = 0; i < newBall.length; i++) { //Loops through all mrect objects
newBall[i].display();
}
for (int i = 0; i < newBall.length; i++) { //Loops through all mrect objects
newBall[i].movement();
}
}
void mousePressed(){
Ball ballInstance = new Ball();
newBall = (Ball[]) append(newBall, ballInstance);
}
class Ball{
float xpos, ypos, xspeed, yspeed;
Ball(){
xpos = width/2;
ypos = height/2;
xspeed = 2;
yspeed = 2.5;
println("created new ball");
}
void display(){
background(100,100,100);
fill(143,154,189);
ellipseMode(CENTER);
ellipse(xpos, ypos, 50,50);
}
void movement(){
xpos = xpos + xspeed;
ypos = ypos + yspeed;
if (xpos > width - 25 || xpos < 25){
xspeed *= -1; }
if (ypos > height - 25 || ypos < 25){
yspeed *= -1; }
}
}
I feel my issue is with the initiation of the object or with the "void draw" function . What am I doing wrong??
Calling the background() function clears everything from the screen.
You call background() from inside the display() function in the Ball class. That means you draw each ball, but the last ball clears out all of the previous balls.
Move that line to inside the main draw() function.
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
I am writing a game engine for Javascript and am attempting to allow my sprites to be rotated.
However, when I rotate my sprites, the image gets skewed. It rotates, but it is not the correct dimensions.
I am following the same basic logic that I use when programming in Java with the Image object and 2d libraries but getting different results. (Image skewed and shouldn't be... just needs to be rotated)
function Sprite(imgg,w,h)
{
this.img = imgg;
this.x = 350;//Math.random()*700;
this.y = 350;//Math.random()*700;
this.vx = 0;//Math.random()*8-4;
this.vy = 0;//Math.random()*8-4;
this.width = w;
this.height = h;
this.rotatespeed = 0;//0.01;
this.rotate = 40;
}
function drawSprite(sprite, ctx)
{
ctx.save();
ctx.translate(sprite.x,sprite.y);
ctx.rotate(sprite.rotate);
ctx.drawImage(sprite.img,0,0,sprite.img.width,sprite.img.height,-sprite.width/2,-sprite.height,sprite.width,sprite.height);
ctx.restore();
}
function drawGame(g)
{
var gameLoop = setInterval(function(){
g.context.clearRect(0,0,g.canvas.width, g.canvas.height);
g.context.save();
g.context.translate(g.canvas.width/2, g.canvas.height/2);
g.context.scale(g.camera.scale,g.camera.scale);
g.context.rotate(g.camera.rotate);
g.context.translate(g.camera.x,g.camera.y);
for(var i=0;i<g.objects.length;i++)
{
updateSprite(g.objects[i]);
drawSprite(g.objects[i], g.context);
}
g.context.restore();
},setIntervalAmount);
}
Turns out that everything was okay, but the web browser was very stretched in one dimension because I had the developer console up, so it was all an optical illusion.
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.