How do i get two canvas-animations to collide? - javascript

im trying to make a simpel game where i can shoot balls at other bals and then they dissapear. i have managed to make the animations work and i can shoot the balls but i dont know how to make them collide.
i have tried to do somethong at line 72-74 but i get the error "Cannot read property 'y' of undefined ".
to see demo the game click the link DEMO
var canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
var tileldig = Math.floor((Math.random() * 300) + 1);
var kuler = [
{r: 10, x: canvas.width/2, y: canvas.height-100, f: "red", dy:0},
//{r: 50, x: tileldig, y: 50, vx:0 , vy: 3, f: "green"},
]
var fiender = [
{r: 10, x: tileldig, y: 50, vx:0 , vy: 1, },
]
var skudder = [
{r: 10, x:0+kuler.x, y: 0+kuler.y, f: "black"},
]
function spill() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < kuler.length; i++) {
kuler[i].x += 0;
kuler[i].y += kuler[i].dy;
ctx.fillStyle = kuler[i].f;
ctx.beginPath();
ctx.arc(kuler[i].x, kuler[i].y, kuler[i].r, 2*Math.PI, 0);
ctx.closePath();
ctx.fill();
if (kuler[0].x >= canvas.width-kuler[0].r) {
kuler[0].x = canvas.width-kuler[0].r
};
if (kuler[0].x <= 0+kuler[0].r) {
kuler[0].x = 0+kuler[0].r
};
if (kuler[0].y >= canvas.height-kuler[0].r) {
kuler[0].y = canvas.height-kuler[0].r
};
if (kuler[0].y <= 0+kuler[0].r) {
kuler[0].y = 0+kuler[0].r
};
};
document.onkeydown = function tast (e) {
switch (e.keyCode) {
case 37:
kuler[0].x -= 10;
break;
case 39:
kuler[0].x += 10;
break;
case 38:
kuler[0].y -= 10;
break;
case 40:
kuler[0].y += 10;
break;
case 32:
newskudd()
console.log("hit space")
if(fiender[i].y >= skudder[1].y){
alert();
};
break;
}
};
for (var i = 0; i < fiender.length; i++) {
ctx.fillStyle = "blue";
ctx.beginPath();
ctx.arc(fiender[i].x, fiender[i].y, fiender[i].r, 2*Math.PI, 0);
ctx.closePath();
ctx.fill();
fiender[i].y += fiender[i].vy;
if (fiender[i].y >= canvas.height) {
fiender.splice(i,1);
console.log("ute");
};
}
requestAnimationFrame(spill);
}
function newskudd () {
var nyttskudd =
{x:kuler[0].x, y:kuler[0].y, r:5, dy:-5, f:"black"};
kuler.push(nyttskudd);
};
setInterval(function(){
fiender.push({r: 10, x: Math.floor((Math.random() * 300) + 1), y: 0, vx:0 , vy: 1, f: "green"});
}, 1000);
spill();
/*if (circles.x >= canvas.height- circles.r){
circles.splice(i,1);
}*/

This is the problem line:
if(fiender[i].y >= skudder[1].y){
You are outside of the loop here, so fiender[i] makes no sense. The quickest fix would be to loop through all the fiender items here using a for loop, just like you're doing 5-6 lines afterwards. Like this:
for (var i = 0; i < fiender.length; i++) {
if(fiender[i].y >= skudder[1].y){
alert();
};
}
Also, skudder[1] doesn't seem to exist, maybe it should be skudder[0].
You need to provide more info to get more accurate answers.

UPDATE: CODE sample as I promised
Sorry, I had to rewrite a lot of your code to make things more clean for me. I tried to keep your origin names.
Line 72-74 is not the place, where you can calculate collision ;)
I will try to give you few hints.
Delete this part, you dont need it.
console.log("hit space")
if(fiender[i].y >= skudder[1].y){
alert();
};
Each time you shoot, you add new bullet in "Array of kulers", which is already done in your function newskudd().
I guess you dont want to kill your enemy when space being hit, but when one ball meets another.
So in spill() go thrue all "bullets" and try to find out, if any of enemies is being hit. ==> every redraw your program will test, if anything is being hit
JS I made in update:
// constants
var TILELDIG = Math.floor((Math.random() * 300) + 1);
var NEW_ENEMY_INTERVAL = 1000;
var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");
// basic graphic constructs (prototypes)
var EntityBall = function (position, color, delta, speed, size) {
this.size = size || 10;
this.position = position || [0, 0];
this.delta = delta || [0, 0];
this.speed = speed || [0, 0];
this.color = color || "black";
};
var Kuler = function (position) {
EntityBall.call(this, position, "black", [0, -10], [0, 0], 5);
};
var Fiender = function (position) {
EntityBall.call(this, position, "blue", [0, 1]);
};
var Skudder = function (position) {
EntityBall.call(this, position, "red", [0, 0], [5, 5]);
}
// Program constructor
var Program = function (ctx, canvas) {
this.ctx = ctx;
this.canvas = canvas;
this.init();
};
// Program inicialization
Program.prototype.init = function () {
// these arrays store living things
this.kulers = [];
this.fienders = [];
this.skudders = [];
this.hordeUpdate = null;
var player1 = new Skudder([canvas.width*0.5, canvas.height*0.75 ]);
this.skudders.push(player1);
// here you bind keys for moving main ball (player1)
document.addEventListener("keydown", this.player1Actions.bind(this, player1));
};
// handle player moves
Program.prototype.player1Actions = function (player1, e) {
switch (e.keyCode) {
case 37:
player1.position[0] -= player1.speed[0];
break;
case 39:
player1.position[0] += player1.speed[0];
break;
case 38:
player1.position[1] -= player1.speed[1];
break;
case 40:
player1.position[1] += player1.speed[1];
break;
case 32:
this.attack(player1);
console.log("hit space");
break;
}
if(player1.position[0] < 0) {
player1.position[0] = 0;
}
if(player1.position[0] > canvas.width) {
player1.position[0] = canvas.width;
}
if(player1.position[1] < 0) {
player1.position[1] = 0;
}
if(player1.position[1] > canvas.height) {
player1.position[1] = canvas.height;
}
};
// only spawns bullet, but doesnt calculate collision
Program.prototype.attack = function (player) {
var kulerPosition = [player.position[0], player.position[1]];
var kuler = new Kuler(kulerPosition);
this.kulers.push(kuler);
};
// main Program thread (refreshed 60 times per second)
Program.prototype.refresh = function () {
var canvas = this.canvas;
this.ctx.clearRect(0, 0, canvas.width, canvas.height);
// update all positions (delta move of each entity)
var list = [this.kulers, this.fienders, this.skudders];
for (var i = 0; i < list.length; i++) {
for (var j = 0; j < list[i].length; j++) {
// find new position
list[i][j].position[0] += list[i][j].delta[0];
list[i][j].position[1] += list[i][j].delta[1];
// delete entity if it is out of canvas (else all browser memory will be eaten by blue balls)
if (list[i][j].position[1] > canvas.height || list[i][j].position[1] < 0 || list[i][j].position[0] > canvas.width || list[i][j].position[0] < 0) {
list[i].splice(j, 1); // this delete it
};
}
}
// calculate bullets collision
for (var i = 0; i < this.kulers.length; i++) { // go thrue all bullets
for (var j = 0; j < this.fienders.length; j++) { // for each bullet go thrue all enemies
// find if this enemy is being hit
// (5 + 2.5), 5 is r of blue ball, 2.5 is r of black ball, you dont want to find perfect hit, scratches counts too
if ((this.kulers[i].position[0] >= this.fienders[j].position[0] - (5 + 2.5) && this.kulers[i].position[0] <= this.fienders[j].position[0] + (5 + 2.5)) && this.kulers[i].position[1] <= this.fienders[j].position[1]) {
this.kulers.splice(i, 1); // delete bullet that hits
this.fienders.splice(j, 1); // delete dead enemy
break;
}
}
}
// after all removes, draw all living entities
for (var i = 0; i < list.length; i++) {
for (var j = 0; j < list[i].length; j++) {
this.ctx.fillStyle = list[i][j].color;
ctx.beginPath();
ctx.arc(list[i][j].position[0], list[i][j].position[1], list[i][j].size, 2 * Math.PI, 0);
ctx.closePath();
ctx.fill();
}
}
// repeat
requestAnimationFrame(this.refresh.bind(this));
};
// this will start blue enemies coming
Program.prototype.hordeComing = function () {
this.hordeUpdate = setInterval(this.addEnemy.bind(this), NEW_ENEMY_INTERVAL);
};
// this spawn enemy
Program.prototype.addEnemy = function () {
var position = [Math.floor(Math.random() * canvas.width / 2) * 2, 0];
var fiender = new Fiender(position);
this.fienders.push(fiender);
};
// run program with params
var program1 = new Program(ctx, canvas);
program1.refresh();
program1.hordeComing();

Related

problem converting LSystems code from p5js to HTML Canvas javascript

I'm trying to convert this Daniel Shiffman tutorial from p5js to html5 canvas without a library:
var angle;
var axiom = "F";
var sentence = axiom;
var len = 100;
var rules = [];
rules[0] = {
a: "F",
b: "FF+[+F-F-F]-[-F+F+F]"
}
function generate() {
len *= 0.5;
var nextSentence = "";
for (var i = 0; i < sentence.length; i++) {
var current = sentence.charAt(i);
var found = false;
for (var j = 0; j < rules.length; j++) {
if (current == rules[j].a) {
found = true;
nextSentence += rules[j].b;
break;
}
}
if (!found) {
nextSentence += current;
}
}
sentence = nextSentence;
createP(sentence);
turtle();
}
function turtle() {
background(51);
resetMatrix();
translate(width / 2, height);
stroke(255, 100);
for (var i = 0; i < sentence.length; i++) {
var current = sentence.charAt(i);
if (current == "F") {
line(0, 0, 0, -len);
translate(0, -len);
} else if (current == "+") {
rotate(angle);
} else if (current == "-") {
rotate(-angle)
} else if (current == "[") {
push();
} else if (current == "]") {
pop();
}
}
}
function setup() {
createCanvas(400, 400);
angle = radians(25);
background(51);
createP(axiom);
turtle();
var button = createButton("generate");
button.mousePressed(generate);
}
This is my code and there is a problem with it that I am not able to debug... When I run the following code, I get the start of this tree with an error message prompting me to change rotate(-this.angle) to call the context. But when I fix the issue, the tree disappears & the rectangle in the "buildFlower()" function appears... why is this happening? :/ :
My code:
const init = () => {
const html = document.getElementsByTagName("html").item(0),
canvas = document.getElementsByTagName("canvas").item(0),
c = canvas.getContext("2d");
let flower;
const gui = new dat.GUI();
function line(x1, y1, x2, y2, color) {
c.strokeStyle = color;
c.beginPath();
c.moveTo(x1, y1);
c.lineTo(x2, y2);
c.stroke();
}
class Flower {
constructor(x, y, size) {
this.x = x;
this.y = y;
this.size = size;
this.angle = (Math.PI / 180) * 25;
this.axiom = "F";
this.sentence = this.axiom;
this.len = 100;
this.rules = [];
this.rules[0] = {
a: "F",
b: "FF+[+F-F-F]-[-F+F+F]",
};
}
generateLSystem() {
this.len *= 0.5;
let nextSentence = "";
for (let i = 0; i < this.sentence.length; i++) {
let current = this.sentence.charAt(i);
let found = false;
for (let j = 0; j < this.rules.length; j++) {
if (current == this.rules[j].a) {
found = true;
nextSentence += this.rules[j].b;
break;
}
}
if (!found) {
nextSentence += current;
}
}
this.sentence = nextSentence;
this.turtle();
}
turtle() {
c.resetTransform();
c.translate(100, getResolution().h);
for (let i = 0; i < this.sentence.length; i++) {
let current = [this.sentence.charAt(i)];
if (current == "F") {
line(0, 0, 0, -this.len);
c.translate(0, -this.len);
} else if (current == "+") {
c.rotate(this.angle);
} else if (current == "-") {
// WHEN I CHANGE THE LINE BELOW TO (`c.rotate`) THE ENTIRE THING DISAPPEARS.
rotate(-this.angle);
} else if (current == "[") {
current.push();
} else if (current == "]") {
current.pop();
}
}
}
buildFlower() {
c.beginPath();
c.rect(
getResolution().w / 2 - this.size / 2,
getResolution().h / 2 - this.size / 2,
this.size,
this.size
);
c.stroke();
}
}
const resize = () => {
canvas.width = w = window.innerWidth;
canvas.height = h = window.innerHeight;
console.log(`screen resolution: ${w}px × ${h}px`);
};
const getResolution = () => {
return { w: canvas.width, h: canvas.height };
};
const setup = () => {
c.clearRect(0, 0, getResolution().w, getResolution().h);
flower = new Flower(getResolution().w, getResolution().h, 200);
flower.generateLSystem();
gui.add(flower, "size", 0, 200);
gui.add(flower, "axiom");
};
setup();
const draw = (t) => {
c.fillStyle = "rgba(255, 255, 255, .5)";
c.fillRect(0, 0, w, h);
window.requestAnimationFrame(draw);
};
let w,
h,
last,
i = 0,
start = 0;
window.removeEventListener("load", init);
window.addEventListener("resize", resize);
resize();
window.requestAnimationFrame(draw);
};
window.addEventListener("load", init);
I've been trying for hrs and can't seem to debug :/
General suggestion: work in small sprints and run the code frequently to verify that it behaves as expected. The code here seems like it was built up in just a few long sprints without much validation for each component along the way. This apparently led to a buildup of complexity, causing confusion and subtle bugs.
Try to avoid premature abstractions like excessive functions and classes until the code is working correctly. At that point, the correct abstractions and cut points will be apparent.
Also, minimize the problem space by only introducing "bells and whistles"-type peripheral functionality once the core logic is working. When the basic drawing is failing, functionality like a GUI, resizing, a RAF loop and so forth just get in the way of progress. Add those after the basic drawing works.
The main reason for the disappearing drawing on the canvas is that resize() is called after setup(). When you resize the canvas, it wipes it clean. Call setup() after your initial resize() call:
window.removeEventListener("load", init);
window.addEventListener("resize", resize);
resize(); // <-- wipes the screen
setup(); // <-- draw after resize
window.requestAnimationFrame(draw);
You see a partial drawing showing up because your crash prevents the resize() from executing. This should be a big debug hint: some code after the drawing must be wiping it out.
The translation from p5's push() and pop() to HTML5 canvas doesn't appear correct:
} else if (current == "[") {
current.push();
} else if (current == "]") {
current.pop();
}
current is an array, but we don't want to mess with it. We're supposed to be pushing and popping the canvas context, not an array. These calls are context.save() and context.restore() in HTML5:
} else if (current === "[") {
c.save();
} else if (current === "]") {
c.restore();
}
A strange design choice is:
let current = [this.sentence.charAt(i)];
if (current == "F") {
Here, you've created an array of one element, then used coercion to compare it to a string. Always use === and don't create a one-element array unnecessarily:
// use const instead of let and [i] instead of charAt(i)
const current = this.sentence[i];
if (current === "F") {
I could analyze the design further and do a full rewrite/code review, but I'll leave it at this just to get you moving again and not belabor the point:
const init = () => {
const html = document.getElementsByTagName("html").item(0),
canvas = document.getElementsByTagName("canvas").item(0),
c = canvas.getContext("2d");
let flower;
// const gui = new dat.GUI();
function line(x1, y1, x2, y2, color) {
c.strokeStyle = color;
c.beginPath();
c.moveTo(x1, y1);
c.lineTo(x2, y2);
c.stroke();
}
class Flower {
constructor(x, y, size) {
this.x = x;
this.y = y;
this.size = size;
this.angle = (Math.PI / 180) * 25;
this.axiom = "F";
this.sentence = this.axiom;
this.len = 50;
this.rules = [];
this.rules[0] = {
a: "F",
b: "FF+[+F-F-F]-[-F+F+F]",
};
}
generateLSystem() {
this.len *= 0.5;
let nextSentence = "";
for (let i = 0; i < this.sentence.length; i++) {
let current = this.sentence[i];
let found = false;
for (let j = 0; j < this.rules.length; j++) {
if (current == this.rules[j].a) {
found = true;
nextSentence += this.rules[j].b;
break;
}
}
if (!found) {
nextSentence += current;
}
}
this.sentence = nextSentence;
this.turtle();
}
turtle() {
c.resetTransform();
c.translate(100, getResolution().h);
for (let i = 0; i < this.sentence.length; i++) {
const current = this.sentence[i];
if (current === "F") {
line(0, 0, 0, -this.len);
c.translate(0, -this.len);
} else if (current === "+") {
c.rotate(this.angle);
} else if (current === "-") {
c.rotate(-this.angle);
} else if (current === "[") {
c.save();
} else if (current === "]") {
c.restore();
}
}
}
buildFlower() {
c.beginPath();
c.rect(
getResolution().w / 2 - this.size / 2,
getResolution().h / 2 - this.size / 2,
this.size,
this.size
);
c.stroke();
}
}
const resize = () => {
canvas.width = w = window.innerWidth;
canvas.height = h = window.innerHeight;
console.log(`screen resolution: ${w}px × ${h}px`);
};
const getResolution = () => {
return { w: canvas.width, h: canvas.height };
};
const setup = () => {
c.clearRect(0, 0, getResolution().w, getResolution().h);
flower = new Flower(getResolution().w, getResolution().h, 200);
flower.generateLSystem();
//gui.add(flower, "size", 0, 200);
//gui.add(flower, "axiom");
};
const draw = (t) => {
c.fillStyle = "rgba(255, 255, 255, .5)";
c.fillRect(0, 0, w, h);
window.requestAnimationFrame(draw);
};
let w,
h,
last,
i = 0,
start = 0;
window.removeEventListener("load", init);
window.addEventListener("resize", resize);
resize();
setup();
window.requestAnimationFrame(draw);
};
window.addEventListener("load", init);
<canvas></canvas>

Stopping animated rain in Canvas smmoothly

Rain Picture HereSo my issue isn't stopping the rain its stopping the making of the rain so the already rendered rain completes its animation off the screen.
I tried setInterval and setTimeout in JS but it just freezes.
And using JQuery to remove canvas tag takes off all drops at once.
Any ideas or direction would be great!
var canvas = document.getElementById("rainCanvas");
var ctx = canvas.getContext("2d");
var canvasW = window.innerWidth;
var canvasH = window.innerHeight;
canvas.height = canvasH;
canvas.width = canvasW;
var mf = 70;
var drops = [];
for(var i = 0; i < mf; i++){
drops.push({
x: Math.random()*canvasW,
y: Math.random()*canvasH,
r: Math.random()*5+2,
d: Math.random() + 1
})
}
function fill() {
ctx.fill();
}
function drawRain(){
ctx.clearRect(0, 0, canvasW, canvasH);
ctx.fillStyle = "rgba(255, 255, 255, .5)";
ctx.beginPath();
for(var i = 0; i < mf; i++){
var f = drops[i];
ctx.moveTo(f.x-5, f.y);
ctx.lineTo(f.x, f.y-15);
ctx.lineTo(f.x+5, f.y);
ctx.arc(f.x, f.y + f.r*.7,5, 0, Math.PI, false);
}
fill();
moveRain();
}
function moveRain(){
for(var i = 0; i < mf; i++){
var f = drops[i];
f.y += Math.pow(f.d, 2) + 1;
if(f.y > canvasH){
drops[i] = {x: Math.random()*canvasW, y: 0, r: f.r, d: f.d};
}
}
}
var i = setInterval(drawRain, 20);
setTimeout(function( ) { clearInterval(); }, 2000);
canvas{ background-color: black }
<canvas id="rainCanvas"></canvas>
What you want to do is set a flag to stop the rain from wrapping back to the top in your function to stop raining:
var stopRain = false;
...
setTimeout(function( ) { stopRain = true; }, 2000);
Now inside moveRain when that variable is true instead of moving it back to the top remove it from the array. Once we removed all the drops we can then clear the interval as it's no longer needed:
function moveRain(){
for(var i = 0; i < mf; i++){
var f = drops[i];
f.y += Math.pow(f.d, 2) + 1;
if(f.y > canvasH){
if(stopRain) { // When we stop raining
drops.splice(i, 1); // Remove drop from array
mf--; // Make sure to update the "length"
if(mf<1) clearInterval(i); // If there are not more drops clear the interval
} else drops[i] = {x: Math.random()*canvasW, y: 0, r: f.r, d: f.d};
}
}
}
Fiddle Example
You could also use drops.length instead of using mf, this way you don't need to do mf--.
Here I fixed some of the code and added a rainDensity variable which controls the count of new raindrops. The original code was designed to have a fixed amount of raindrops, it had to be changed to achieve the desired effect.
window.onload = function(){
var canvas = document.getElementById("rainCanvas");
var ctx = canvas.getContext("2d");
var canvasW = window.innerWidth;
var canvasH = window.innerHeight;
canvas.height = canvasH;
canvas.width = canvasW;
var drops = [];
function makeDrop() {
drops.push({
x: Math.random()*canvasW,
y: -10,
r: Math.random()*4+1,
d: Math.pow(Math.random() + 1, 2) + 1
})
}
function drawRain(){
ctx.clearRect(0, 0, canvasW, canvasH);
ctx.fillStyle = "rgba(128, 128, 255, .5)";
for(var f of drops){
ctx.beginPath();
ctx.moveTo(f.x-5, f.y);
ctx.lineTo(f.x, f.y-15);
ctx.lineTo(f.x+5, f.y);
ctx.arc(f.x, f.y + f.r*.7,5, 0, Math.PI, false);
ctx.fill();
}
}
function handleFrame() {
drawRain();
updateRain();
}
function updateRain() {
moveRain();
makeNewDrops();
}
var dropPerFrameCounter = 0;
var startTime = Date.now();
function makeNewDrops() {
var elapsedTime = (Date.now() - startTime) / 1000;
// rainDensity: set it to 0 to stop rain
var rainDensity = Math.max(0, Math.sin(elapsedTime / 3) + 0.5);
dropPerFrameCounter += rainDensity;
while (dropPerFrameCounter >= 1) {
dropPerFrameCounter--;
makeDrop();
}
}
function moveRain() {
for(var f of drops){
f.y += f.d;
}
drops = drops.filter(d => d.y < canvasH);
}
var intervalRender = setInterval(handleFrame, 20);
}
<canvas id="rainCanvas"></canvas>
Had to add an answer because the example all used setInterval to do the animation which looks awful. Use requestAnimationFrame to animate. Also fixed some other problems with the code.
I have added a pause the pauses the animation when you click on the canvas. Clicking again will continue.
var canvas = document.getElementById("rainCanvas");
var ctx = canvas.getContext("2d");
var canvasW = window.innerWidth;
var canvasH = window.innerHeight;
canvas.height = canvasH;
canvas.width = canvasW;
var mf = 70;
var drops = [];
var paused = false;
for(var i = 0; i < mf; i++){
drops.push({
x: Math.random()*canvasW,
y: Math.random()*canvasH,
r: Math.random()*5+2,
d: Math.random() + 1
})
}
function drawRain(){
if(!paused){
ctx.clearRect(0, 0, canvasW, canvasH);
ctx.fillStyle = "rgba(255, 255, 255, .5)";
ctx.beginPath();
for(var i = 0; i < mf; i++){
var f = drops[i];
ctx.moveTo(f.x - 2.5 * f.d, f.y);
ctx.lineTo(f.x, f.y - 7.5 * f.d);
ctx.lineTo(f.x + 2.5 * f.d, f.y);
ctx.arc(f.x, f.y + f.r * 0.7, 2.5 * f.d, 0, Math.PI, false);
}
ctx.fill();
for(var i = 0; i < mf; i++){
var f = drops[i];
f.y += Math.pow(f.d, 2) + 1;
if(f.y > canvasH + 15){ // make sure completely off the bottom
// dont create a new drop reuse it will stop GC from
// having to interrupt the code.
f.x = Math.random()*canvasW;
f.y = -6; // move off top
}
}
}
requestAnimationFrame(drawRain);
}
requestAnimationFrame(drawRain);
canvas.addEventListener("click",()=>{paused = ! paused});
canvas{
background-color: black;
position : absolute;
top : 0px;
left : 0px;
}
<canvas id="rainCanvas"></canvas>

Snake game: how to check if the head collides with its own body

You may have played Snake, a game where you have to eat food to grow and you fail if you collide with the snake's body or certain obstacles. The first part was easy, but the latter seems impossible to achieve.
I have tried to make a for loop check if the last element of my snake array is colliding with its other parts. My condition was like this: if the x position of the last item in my array is bigger than any of the array items' x position, and smaller than their x position plus their width, and so on. That didn't work.
Here's my code :
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<canvas id="myCanvas" width="200px" height="200px" style="border:1px solid black"/>
<script>
var canvas = document.getElementById("myCanvas");
var ctx = canvas.getContext("2d");
var yPos = 20;
var width = 15;
var variable = 1;
var currentDir = 1;
//var xPos = (width+5)*variable;
var xPos = 20;
var myArr = [{myX:xPos,myY:yPos},{myX:xPos,myY:yPos},{myX:xPos,myY:yPos}];
var downPressed = false;
var upPressed = false;
var leftPressed = false;
var rightPressed = false;
var first = [0,20,40,60,80,100,120,140,160,180];
var firstX = Math.floor(Math.random()*10);
var firstY = Math.floor(Math.random()*10);
var okayed = first[firstX];
var notOkayed = first[firstY];
var maths = myArr[myArr.length-1];
function drawFood() {
ctx.beginPath();
ctx.rect(okayed,notOkayed,15,15);
ctx.fillStyle = "red";
ctx.fill();
ctx.closePath();
}
function drawRectangle() {
ctx.clearRect(0,0,200,200);
drawFood();
for(var i = 0;i<myArr.length;i++) {
ctx.beginPath();
ctx.rect(myArr[i].myX,myArr[i].myY,width,15);
ctx.fillStyle = "blue";
ctx.fill();
ctx.closePath();
}
requestAnimationFrame(drawRectangle);
}
setInterval("calledin()",100);
function calledin() {
var secondX = Math.floor(Math.random()*10);
var secondY = Math.floor(Math.random()*10);
var newobj = {myX:myArr[myArr.length-1].myX+20,myY:myArr[myArr.length-1].myY};
var newobjTwo = {myX:myArr[myArr.length-1].myX,myY:myArr[myArr.length-1].myY+20};
var newobjLeft = {myX:myArr[myArr.length-1].myX-20,myY:myArr[myArr.length-1].myY};
var newobjUp = {myX:myArr[myArr.length-1].myX,myY:myArr[myArr.length-1].myY-20};
var okayNewObj = {myX:myArr[1].myX - 20,myY:myArr[1].myY};
if(myArr[myArr.length-1].myX > 180 || myArr[myArr.length-1].myX < 0 || myArr[myArr.length-1].myY > 180 || myArr[myArr.length-1].myY < 0)
{alert("Game Over");window.location.reload();}
if(myArr[myArr.length-1].myX > okayed-5 && myArr[myArr.length-1].myX < okayed+20 && myArr[myArr.length-1].myY < notOkayed+20 &&
myArr[myArr.length-1].myY > notOkayed-5) {
okayed = first[secondX];
notOkayed = first[secondY];
myArr.unshift(okayNewObj);
}
if(currentDir == 1) {
myArr.push(newobj);
myArr.shift();}
if(currentDir == 2) {
myArr.push(newobjTwo);
myArr.shift();
}
if(currentDir == 4) {
myArr.push(newobjLeft);
myArr.shift();
}
if(currentDir == 3) {
myArr.push(newobjUp);
myArr.shift();
}
for(var i = 0;i<myArr.length-2;i++) {
if(myArr[myArr.length-1].myX > myArr[i].myX &&
myArr[myArr.length-1].myX < myArr[i].myX + 15 && myArr[myArr.length-1].myY > myArr[i].myY && myArr[myArr.length-1].myY > myArr[i].myY + 15)
{alert("Game over");window.location.reload();}
}
}
function downed(e) {
if(e.keyCode==40) {if(currentDir != 3) {currentDir = 2;}}
if(e.keyCode==38) {if(currentDir != 2) {currentDir = 3;}}
if(e.keyCode==39) {if(currentDir != 4) {currentDir = 1;}}
if(e.keyCode==37) {if(currentDir != 1) {currentDir = 4;}}
}
function upped(e) {
if(e.keyCode == 40) {downPressed = false;}
}
document.addEventListener("keydown",downed,false);
document.addEventListener("keyup",upped,false);
drawRectangle();
</script>
</body>
</html>
Suppose the snake is represented by an array called snake in which the head is at index snake.length - 1. We have to compare the position of the head against the positions of the body segments at indices 0 through snake.length - 2.
The following code sets okay to false if the snake head has collided with a body segment. Otherwise, okay remains true.
var head = snake[snake.length - 1],
x = head.x,
y = head.y,
okay = true;
for (var i = snake.length - 2; i >= 0; --i) {
if (snake[i].x == x && snake[i].y == y) {
okay = false;
break;
}
}
Below is a snippet in which I have modified your code to clarify the game logic and to simplify many of the calculations.
Instead of working directly with canvas coordinates, I represent each position with the column index x and row index y of a virtual grid cell. This lets us calculate the neighboring grid positions by adding 1 or -1 to x or y. When it comes time to paint the canvas, we multiply the virtual coordinates by the cell size.
I have replaced most of your literal values with variables. For example, instead of setting the canvas dimensions to 200 by 200, we can do this:
canvas.width = numCols * cellSize;
canvas.height = numRows * cellSize;
This lets us change numCols and numRows in one place to resize the whole game grid. All the calculations work out because they evaluate variables instead of using literals.
I altered the key-event handling to recognize the key codes for the W-A-S-D keys in addition to the arrow keys. When the game is embedded in a long web page, as it is here, you'll probably want to use the W-A-S-D keys so that the page doesn't scroll up and down while you're playing.
var canvas,
ctx,
currentDir,
startX = 1,
startY = 1,
startSnakeLength = 3,
snake,
cellSize = 18,
cellGap = 1,
foodColor = '#a2302a',
snakeBodyColor = '#2255a2',
snakeHeadColor = '#0f266b',
numRows = 10,
numCols = 10,
canvasWidth = numCols * cellSize,
canvasHeight = numRows * cellSize;
var food = {};
function placeFood() {
// Find a random location that isn't occupied by the snake.
var okay = false;
while (!okay) {
food.x = Math.floor(Math.random() * numCols);
food.y = Math.floor(Math.random() * numRows);
okay = true;
for (var i = 0; i < snake.length; ++i) {
if (snake[i].x == food.x && snake[i].y == food.y) {
okay = false;
break;
}
}
}
}
function paintCell(x, y, color) {
ctx.fillStyle = color;
ctx.fillRect(x * cellSize + cellGap,
y * cellSize + cellGap,
cellSize - cellGap,
cellSize - cellGap);
}
function paintCanvas() {
ctx.clearRect(0, 0, canvasWidth, canvasHeight);
paintCell(food.x, food.y, foodColor);
var head = snake[snake.length - 1];
paintCell(head.x, head.y, snakeHeadColor);
for (var i = snake.length - 2; i >= 0; --i) {
paintCell(snake[i].x, snake[i].y, snakeBodyColor);
}
}
function updateGame() {
var head = snake[snake.length - 1],
x = head.x,
y = head.y;
// Move the snake.
var tail = snake.shift();
switch (currentDir) {
case 'up':
snake.push(head = { x: x, y: y - 1 });
break;
case 'right':
snake.push(head = { x: x + 1, y: y });
break;
case 'down':
snake.push(head = { x: x, y: y + 1 });
break;
case 'left':
snake.push(head = { x: x - 1, y: y });
break;
}
paintCanvas();
x = head.x;
y = head.y;
// Check for wall collision.
if (x < 0 || x >= numCols || y < 0 || y >= numRows) {
stopGame('wall collision');
return;
}
// Check for snake head colliding with snake body.
for (var i = snake.length - 2; i >= 0; --i) {
if (snake[i].x == x && snake[i].y == y) {
stopGame('self-collision');
return;
}
}
// Check for food.
if (x == food.x && y == food.y) {
placeFood();
snake.unshift(tail);
setMessage(snake.length + ' segments');
}
}
var dirToKeyCode = { // Codes for arrow keys and W-A-S-D.
up: [38, 87],
right: [39, 68],
down: [40, 83],
left: [37, 65]
},
keyCodeToDir = {}; // Fill this from dirToKeyCode on page load.
function keyDownHandler(e) {
var keyCode = e.keyCode;
if (keyCode in keyCodeToDir) {
currentDir = keyCodeToDir[keyCode];
}
}
function setMessage(s) {
document.getElementById('messageBox').innerHTML = s;
}
function startGame() {
currentDir = 'right';
snake = new Array(startSnakeLength);
snake[snake.length - 1] = { x: startX, y: startY };
for (var i = snake.length - 2; i >= 0; --i) {
snake[i] = { x: snake[i + 1].x, y: snake[i + 1].y + 1 };
}
placeFood();
paintCanvas();
setMessage('');
gameInterval = setInterval(updateGame, 200);
startGameButton.disabled = true;
}
function stopGame(message) {
setMessage(message + '<br> ended with ' + snake.length + ' segments');
clearInterval(gameInterval);
startGameButton.disabled = false;
}
var gameInterval,
startGameButton;
window.onload = function () {
canvas = document.getElementById('gameCanvas'),
ctx = canvas.getContext('2d');
canvas.width = numCols * cellSize;
canvas.height = numRows * cellSize;
Object.keys(dirToKeyCode).forEach(function (dir) {
dirToKeyCode[dir].forEach(function (keyCode) {
keyCodeToDir[keyCode] = dir;
})
});
document.addEventListener("keydown", keyDownHandler, false);
startGameButton = document.getElementById('startGameButton');
startGameButton.onclick = startGame;
}
body {
font-family: sans-serif;
}
#gameCanvas {
border: 1px solid #000;
float: left;
margin-right: 15px;
}
#startGameButton, #messageBox {
font-size: 16px;
margin-top: 15px;
}
#messageBox {
line-height: 24px;
}
<canvas id="gameCanvas"></canvas>
<button id="startGameButton">Start game</button>
<div id="messageBox"></div>

How do i detect collision betwen two animated balls?

I'm trying to make a simple shooter, but I have some problems with detecting collisions between the shots and the enemy (blue balls). I have tried several things, but I can't figure it out
Can someone pliz please help me?
var canvas = document.getElementById("canvas");
ctx = canvas.getContext("2d");
var tileldig = Math.floor((Math.random() * 300) + 1);
var tekst = document.getElementById("tekst")
var kuler = [
{r: 10, x: canvas.width/2, y: canvas.height-100, f: "red", dy:0},
//{r: 50, x: tileldig, y: 50, vx:0 , vy: 3, f: "green"},
]
var fiender = [
{r: 20, x: tileldig, y: -20, vx:0 , vy: 1, },
]
/*var skudder = [
{r: 10, x:0+kuler.x, y: 0+kuler.y, f: "black"},
]*/
function spill() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < kuler.length; i++) {
kuler[i].x += 0;
kuler[i].y += kuler[i].dy;
ctx.fillStyle = kuler[i].f;
ctx.beginPath();
ctx.arc(kuler[i].x, kuler[i].y, kuler[i].r, 2*Math.PI, 0);
ctx.closePath();
ctx.fill();
if (kuler[0].x >= canvas.width-kuler[0].r) {
kuler[0].x = canvas.width-kuler[0].r
};
if (kuler[0].x <= 0+kuler[0].r) {
kuler[0].x = 0+kuler[0].r
};
if (kuler[0].y >= canvas.height-kuler[0].r) {
kuler[0].y = canvas.height-kuler[0].r
};
if (kuler[0].y <= 0+kuler[0].r) {
kuler[0].y = 0+kuler[0].r
};
for (var j = 0; j < fiender.length; j++) {
ctx.fillStyle = "blue";
ctx.beginPath();
ctx.arc(fiender[j].x, fiender[j].y, fiender[j].r, 2*Math.PI, 0);
ctx.closePath();
ctx.fill();
fiender[j].y += fiender[j].vy;
if (fiender[j].y >= canvas.height) {
fiender.splice(j,1);
console.log("ute");
};
if (fiender[j].y + fiender[j].r >= kuler[i].y && fiender[j].x + fiender[j].r == kuler[i].x) { // remove kuler[i] and fiender[j] }
fiender.splice(j, 1);
kuler.splice(i,1);
};
if(j > 1){
fiender.splice(j,1)
}
}
}
document.onkeydown = function tast (e) {
switch (e.keyCode) {
case 37:
kuler[0].x -= 10;
break;
case 39:
kuler[0].x += 10;
break;
case 38:
kuler[0].y -= 10;
break;
case 40:
kuler[0].y += 10;
break;
case 32:
newskudd()
console.log("hit space")
break;
}
};
requestAnimationFrame(spill);
}
function newskudd () {
var nyttskudd =
{x:kuler[0].x, y:kuler[0].y, r:5, dy:-5, f:"black"};
kuler.push(nyttskudd);
};
setInterval(
function(){
fiender.push({r: 20, x: Math.floor((Math.random() * 300) + 1), y: -20, vx:0 , vy: 1, f: "green"});
}, 1000);
spill();
/*for (var i = 0; i < kuler.length; i++) {
for (var j = 0; j < fiender.length; j++) {
if (kuler[i].y >= fiender[j].y) { // remove kuler[i] and fiender[j] }
};
}*/
/*if (circles.x >= canvas.height- circles.r){
circles.splice(i,1);
}*/
If you want to see a demo, click the link: DEMO
Your conditional should look like:
var distanceFromCenters = Math.sqrt(Math.pow(Math.abs(fiender[j].x - kuler[i].x),2) + Math.pow(Math.abs(fiender[j].y - kuler[i].y),2 );
if (distanceFromCenters <= (fiender[j].r + kuler[i].r)) {
// you have a collision

How to add multiple colors to an array

I am trying to add multiple colors to an array in an HTML5 canvas animation. I am using an if statement to add 3 different colors based on the array index, but the only color showing up is the "else" color (orange). Below is the code.
window.onload = function() {
var canvas = document.getElementById('myCanvas'),
context = canvas.getContext('2d'),
w = window.innerWidth,
h = window.innerHeight,
total = 100,
gumballs = [];
canvas.width = w;
canvas.height = h;
for(var i = 0; i < total; i++) {
gumballs.push({
x: Math.random()* w,
y: Math.random()* h,
r: Math.random()* 4+3,
d: Math.random()*total
})
}
function draw() {
context.clearRect(0,0,w,h);
context.beginPath();
for(var i = 0; i < total; i++) {
var s = gumballs[i];
if(s % 3 === 0){
context.fillStyle = 'blue';
} else if (s % 4 === 0) {
context.fillStyle = 'red';
} else {
context.fillStyle = "orange";
}
context.moveTo(s.x, s.y);
context.arc(s.x, s.y, s.r, 0, Math.PI*2, true);
}
context.fill();
update();
}
function update() {
for(var i = 0; i < total; i++) {
var s = gumballs[i];
s.y += 8;
if(s.x > w + 10 || s.x < - 10 || s.y > h) {
gumballs[i] = {x: Math.random()*w, y: 0, r: s.r, d: s.d};
}
}
}
setInterval(draw, 30);
}
You need to do .fill for each gumball rather than one .fill at the end:
function draw() {
context.clearRect(0,0,w,h);
for(var i = 0; i < total; i++) {
var s = gumballs[i];
if((i%3) == 0){
context.fillStyle = 'blue';
} else if ((i%4) == 0) {
context.fillStyle = 'red';
} else {
context.fillStyle = "orange";
}
context.beginPath();
context.moveTo(s.x, s.y);
context.arc(s.x, s.y, s.r, 0, Math.PI*2, true);
context.fill();
}
update();
}
It looks like gumballs[i] will always be an object - {x, y, r, d} - and so s % 3 and s % 4 will be NaN, causing the code to fall to the else clause.
You state you want to set color based on array index so your if should be checking index, not the array element at that index
Try changing:
if(s % 3 === 0){
To:
if(i % 3 === 0){
Same for other if

Categories

Resources