As the title suggests, I am having trouble with object collision...
I am currently working on a 2d Html5 canvas game using JavaScript. I know how to keep the "player" object from going outside the width/height of the game canvas, and i know how to do something when the player collides with an object (such as a power up or enemy or whatever) but i just don't know how to make a "solid" object meaning when the player hits the solid object, the player just stops, and cannot go through the solid object.
This is what I have now (not all the code just what I feel is relevant, sorry if it's too much/too little.:
var canvasPlayer = document.getElementById('canvasPlayer');
var ctxPlayer = canvasPlayer.getContext('2d');
var canvasWalls = document.getElementById('canvasWalls');
var ctxWalls = canvasWalls.getContext('2d');
function checkKeyDown(e) {
var keyID = (e.keyCode) || e.which;
if (keyID === 38 || keyID === 87) { // up arrow OR W key
if (!player1.isDownKey && !player1.isLeftKey && !player1.isRightKey) {
player1.isUpKey = true;
e.preventDefault();
} }
if (keyID === 39 || keyID === 68) { //right arrow OR D key
if (!player1.isDownKey && !player1.isLeftKey && !player1.isUpKey) {
player1.isRightKey = true;
e.preventDefault();
} }
if (keyID === 40 || keyID === 83) {//down arrow OR S key
if (!player1.isUpKey && !player1.isLeftKey && !player1.isRightKey) {
player1.isDownKey = true;
e.preventDefault();
} }
if (keyID === 37 || keyID === 65) {//left arrow OR A key
if (!player1.isDownKey && !player1.isUpKey && !player1.isRightKey) {
player1.isLeftKey = true;
e.preventDefault();
}
}
}
Walls.prototype.draw = function (){
ctxWalls.drawImage(imgSprite,this.srcX,this.srcY,this.width,this.height,this.drawX,this.drawY,this.width,this.height);
this.checkHitPlayer();
};
Walls.prototype.checkHitPlayer = function() {
if (this.drawX > player1.drawX &&
this.drawX <= player1.drawX + player1.width &&
this.drawY >= player1.drawY &&
this.drawY < player1.drawY + player1.height) {
player1.isUpKey = false;
player1.isDownKey = false;
player1.isRightKey = false;
player1.isLeftKey = false;
}
};
This works... except when trying to go up or left, the player only moves maybe 2-3 pixels, so it takes 3 left or up arrows to go left or up. As well the player can move straight through the wall which is not what i want. Any help is much appreciated sorry if i included too much or not enough code. Oh, i also forgot to mention the game is a puzzle game, and I have it set-up so a player can only move one direction at a time until hitting a wall.
If you just want your player to stop when the reach a wall, you can apply some math:
For example: assume your player is a 10px by 10px rectangle and the right wall's X position is 200.
The X position of the right side of the rectangle is calculated like this:
var playerRightSide = player.x + player.width;
You can test if the player has reached the wall like this:
if( playerRightSide >= 200 )
If the user tries to push their player beyond the wall, you would hold the player to the left of the wall using the players X position.
if( playerRightSide >= 200 ) { player.x = 190; }
The 190 is the wall's X position (200) minus the player's width (10).
Read further if you're interested in doing more advanced collision testing.
Many basic game collisions can be classified into 3 types:
Circle versus Circle collision
Rectangle versus Rectangle collision
Rectangle versus Circle collision
Here’s an illustration of how to detect each of these common collisions.
Assume you define a circle like this:
var circle1={
x:30,
y:30,
r:10
};
Assume you define a rectangle like this:
var rect1={
x:20,
y:100,
w:20,
h:20
};
You can detect Circle vs Circle collisions like this...
...Using this Circle vs Circle collision-test code:
// return true if the 2 circles are colliding
// c1 and c2 are circles as defined above
function CirclesColliding(c1,c2){
var dx=c2.x-c1.x;
var dy=c2.y-c1.y;
var rSum=c1.r+c2.r;
return(dx*dx+dy*dy<=rSum*rSum);
}
You can detect Rectangle vs Rectangle collisions like this...
...Using this Rectangle vs Rectangle collision-test code:
// return true if the 2 rectangles are colliding
// r1 and r2 are rectangles as defined above
function RectsColliding(r1,r2){
return !(r1.x>r2.x+r2.w || r1.x+r1.w<r2.x || r1.y>r2.y+r2.h || r1.y+r1.h<r2.y);
}
You can detect Rectangle vs Circle collisions like this...
...Using this Rectangle vs Circle collision-test code:
// return true if the rectangle and circle are colliding
// rect and circle are a rectangle and a circle as defined above
function RectCircleColliding(rect,circle){
var dx=Math.abs(circle.x-(rect.x+rect.w/2));
var dy=Math.abs(circle.y-(rect.y+rect.y/2));
if( dx > circle.r+rect.w2 ){ return(false); }
if( dy > circle.r+rect.h2 ){ return(false); }
if( dx <= rect.w ){ return(true); }
if( dy <= rect.h ){ return(true); }
var dx=dx-rect.w;
var dy=dy-rect.h
return(dx*dx+dy*dy<=circle.r*circle.r);
}
For example, you can use these collision tests to respond to a player touching a power-up cube:
// create a circular player object
// that's located at [30,30] and has a radius of 10px
var player={x:30,y:30,r:10};
// create a rectangular power-up at position [200,30]
var powerup={x:200, y:30, w:20, h:20};
// Let's say the user keys the player to coordinate [200,35]
// (touching the power-up)
player.x = 220;
player.y = 35;
// you can test if the circular player is touching the rectangular power-up
if( RectCircleColliding(powerup,player) ) {
// the player has collided with the power-up, give bonus power!
player.power += 100;
}
Here is code and a Fiddle: http://jsfiddle.net/m1erickson/u6t48/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<style>
body{ background-color: ivory; padding:20px; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
window.requestAnimFrame = (function(callback) {
return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame ||
function(callback) {
window.setTimeout(callback, 1000 / 60);
};
})();
ctx.fillStyle="lightgray";
ctx.strokeStyle="skyblue";
// top collision circle vs circle
var circle1={x:30,y:30,r:10};
var circle2={x:70,y:40,r:10};
var circle3={x:100,y:30,r:10};
var direction1=1;
// middle collision rect vs rect
var rect1={x:20,y:100,w:20,h:20};
var rect2={x:50,y:110,w:20,h:20};
var rect3={x:90,y:100,w:20,h:20};
var direction2=1;
// bottom collision rect vs circle
var circle4={x:30,y:200,r:10};
var rect4={x:50,y:205,w:20,h:20};
var circle5={x:100,y:200,r:10};
var direction3=1;
function drawAll(){
ctx.clearRect(0,0,canvas.width,canvas.height);
drawCircle(circle1);
drawCircle(circle2);
drawCircle(circle3);
drawCircle(circle4);
drawCircle(circle5);
drawRect(rect1);
drawRect(rect2);
drawRect(rect3);
drawRect(rect4);
}
function drawCircle(c){
ctx.beginPath();
ctx.arc(c.x,c.y,c.r,0,Math.PI*2,false);
ctx.closePath();
ctx.fill();
ctx.stroke();
}
function drawRect(r){
ctx.beginPath();
ctx.rect(r.x,r.y,r.w,r.h);
ctx.closePath();
ctx.fill();
ctx.stroke();
}
// return true if the 2 circles are colliding
function CirclesColliding(c1,c2){
var dx=c2.x-c1.x;
var dy=c2.y-c1.y;
var rSum=c1.r+c2.r;
return(dx*dx+dy*dy<=rSum*rSum);
}
// return true if the 2 rectangles are colliding
function RectsColliding(r1,r2){
return !(r1.x>r2.x+r2.w || r1.x+r1.w<r2.x || r1.y>r2.y+r2.h || r1.y+r1.h<r2.y);
}
// return true if the rectangle and circle are colliding
function RectCircleColliding(rect,circle){
var dx=Math.abs(circle.x-(rect.x+rect.w/2));
var dy=Math.abs(circle.y-(rect.y+rect.h/2));
if( dx > circle.r+rect.w/2 ){ return(false); }
if( dy > circle.r+rect.h/2 ){ return(false); }
if( dx <= rect.w ){ return(true); }
if( dy <= rect.h ){ return(true); }
var dx=dx-rect.w;
var dy=dy-rect.h
return(dx*dx+dy*dy<=circle.r*circle.r);
}
var fps = 15;
function animate() {
setTimeout(function() {
requestAnimFrame(animate);
// circle vs circle
circle2.x = circle2.x+direction1;
if( CirclesColliding(circle2,circle1) || CirclesColliding(circle2,circle3) ){
direction1=-direction1;
}
// rect vs rect
rect2.x = rect2.x+direction2;
if( RectsColliding(rect2,rect1) || RectsColliding(rect2,rect3) ){
direction2=-direction2;
}
// rect vs circle
rect4.x = rect4.x+direction3;
if( RectCircleColliding(rect4,circle4) || RectCircleColliding(rect4,circle5) ){
direction3=-direction3;
}
drawAll();
}, 1000 / fps);
}
animate();
}); // end $(function(){});
</script>
</head>
<body>
<canvas id="canvas" width=300 height=300></canvas>
</body>
</html>
Related
Hey everyone I'm making a snake game in JS. Right Now I'm working on the function that will stop the game if the snake head has hit somewhere on the snake body. since the snakes an array, I loop through every snake unit and compare it with the head unit. The problem is now the game stops before you can even start. Since the snake head is in the array that's being looped through the very first loop compares the head position with itself and since there both at the same position the game stops. Any ideas?
//declare global variables
const canvas = document.querySelector('#canvas');
//set canvas context
const ctx = canvas.getContext('2d');
//put canvas dimensions into variables
const cvsW = canvas.width;
const cvsH = canvas.height;
//create snake unit
const unit = 16;
//create snake and set starting position
let snake = [{
x : cvsW/2,
y : cvsH/2
}]
//create food object and set its position somewhere on board
let food = {
//Math.floor(Math.random()*cvsW + 1)---number from 1 to 784
//Math.floor(Math.random()*cvsW/unit + 1)---number from 1 to 79
//Math.floor(Math.random()*cvsW/unit + 1)*unit---number from 1 to 784(but it's a multiple of unit)
//Math.floor(Math.random()*(cvsW/unit - 1)+1)*unit---same as above but -1 keeps food inside canvas
x : Math.floor(Math.random()*(cvsW/unit - 1)+1)*unit-unit/2,
y : Math.floor(Math.random()*(cvsH/unit - 1)+1)*unit-unit/2
}
//create a variable to store the direction of the snake
let direction;
//add event to read users input then change direction
document.addEventListener('keydown', (e) => {
if(e.keyCode == 37 && direction != 'right') direction = 'left';
else if (e.keyCode == 38 && direction != 'down') direction = 'up';
else if (e.keyCode == 39 && direction != 'left') direction = 'right';
else if (e.keyCode == 40 && direction != 'up') direction = 'down';
})
function draw() {
//clear canvas and redraw snake
ctx.clearRect(0, 0, cvsW, cvsH);
for(let i = 0; i < snake.length; i++) {
ctx.fillStyle = 'limegreen';
ctx.fillRect(snake[i].x-unit/2, snake[i].y-unit/2, unit, unit);
}
//draw food
ctx.fillStyle = 'red';
ctx.fillRect(food.x-unit/2, food.y-unit/2, unit, unit);
//grab heads position
let headX = snake[0].x;
let headY = snake[0].y;
//move snake in chosen direction
if(direction == 'left') headX -= unit;
else if(direction == 'right') headX += unit;
else if(direction == 'up') headY -= unit;
else if(direction == 'down') headY += unit;
//create new snake unit
let newHead = {x : headX, y :headY}
//check to see if snakes eaten food
if(headX === food.x && headY === food.y) {
food = {
x : Math.floor(Math.random()*(cvsW/unit - 1)+1)*unit-unit/2,
y : Math.floor(Math.random()*(cvsH/unit - 1)+1)*unit-unit/2
}
//create 4 new units
for(let i = 4; i > 0; i--) {
//add those units -without this code snake will not grow
snake.unshift(newHead);
}
} else {
//remove tail -without this code snake will keep growing
snake.pop();
}
//add new head position -without this code snake will not move
snake.unshift(newHead);
//check to see if snake has hit a wall or itself
if(headX < 0 || headX > cvsW || headY < 0 || headY > cvsH || collision(headX, headY)) {
clearInterval(runGame);
}
}
let runGame = setInterval(draw, 70);
function collision(x, y) {
for(let i = 0; i < snake.length; i++) {
if(x == snake[i].x && y == snake[i].y) return true;
}
return false;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Snake Game</title>
<style>
body {
background-color: #333;
}
canvas {
background-color: #4d4d4d;
margin: auto;
display: block;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
width: 750px;
height: 500px;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="script.js"></script>
</body>
</html>
Add collision script to ignore the first two links of the snake when checking collisions with itself.
function selfcollision(x, y) {
for(let i = 2; i < snake.length; i++) {
if(x == snake[i].x && y == snake[i].y) return true;
}
return false;
}
Below is a script which defines two functions that draw 4 rectangular buttons and 1 circular button respectively. I am trying to implement specific Hover and Click functionality into the buttons (as described in the script alerts) but I am at a bit of a loss as to how to do this. I tried calling the makeInteractiveButton() functions on each click but this caused a lot of odd overlap and lag. I want the script to do the following:
If the circular button is hovered, I would like it's fillColour to change and if it is clicked I would like it to change again to the colours described in the code (#FFC77E for hover, #FFDDB0 for clicked). This should only happen for the duration of the hover or click.
HTML:
<html lang="en">
<body>
<canvas id="game" width = "750" height = "500"></canvas>
<script type='text/javascript' src='stack.js'></script>
</body>
</html>
JavaScript:
var c=document.getElementById('game'),
canvasX=c.offsetLeft,
canvasY=c.offsetTop,
ctx=c.getContext('2d')
elements = [];
c.style.background = 'grey';
function makeInteractiveButton(x, strokeColor, fillColor) {
ctx.strokeStyle=strokeColor;
ctx.fillStyle=fillColor;
ctx.beginPath();
ctx.lineWidth=6;
ctx.arc(x, 475, 20, 0, 2*Math.PI);
ctx.closePath();
ctx.stroke();
ctx.fill();
elements.push({
arcX: x,
arcY: 475,
arcRadius: 20
});
}
b1 = makeInteractiveButton(235, '#FFFCF8', '#FFB85D');
c.addEventListener('mousemove', function(event) {
x=event.pageX-canvasX; // cursor location
y=event.pageY-canvasY;
elements.forEach(function(element) {
if (x > element.left && x < element.left + element.width &&
y > element.top && y < element.top + element.height) { // if cursor in rect
alert('Rectangle should undergo 5 degree rotation and 105% scale');
}
else if (Math.pow(x-element.arcX, 2) + Math.pow(y-element.arcY, 2) <
Math.pow(element.arcRadius, 2)) { // if cursor in circle
alert('Set b1 fillColour to #FFC77E.');
}
});
}, false);
c.addEventListener('click', function(event) {
x=event.pageX-canvasX; // cursor location
y=event.pageY-canvasY;
elements.forEach(function(element) {
if (x > element.left && x < element.left + element.width &&
y > element.top && y < element.top + element.height) { // if rect clicked
alert('Move all cards to centre simultaneously.');
}
else if (Math.pow(x-element.arcX, 2) + Math.pow(y-element.arcY, 2) <
Math.pow(element.arcRadius, 2)) { // if circle clicked
alert('Set b1 fillColour to #FFDDB0.');
}
});
}, false);
One way is keep all element data and write a hitTest(x,y) function but when you have a lot of complex shapes its better to use a secondary canvas to render element with their ID instead of their color in it and the color of x,y in second canvas is ID of hitted element, I should mention that the second canvas is'nt visible and its just a gelper for get the hitted element.
Github Sample:
https://siamandmaroufi.github.io/CanvasElement/
Simple implementation of hitTest for Rectangles :
var Rectangle = function(id,x,y,width,height,color){
this.id = id;
this.x=x;
this.y=y;
this.width = width;
this.height = height;
this.color = color || '#7cf';
this.selected = false;
}
Rectangle.prototype.draw = function(ctx){
ctx.fillStyle = this.color;
ctx.fillRect(this.x,this.y,this.width,this.height);
if(this.selected){
ctx.strokeStyle='red';
ctx.setLineDash([5,5]);
ctx.lineWidth = 5;
ctx.strokeRect(this.x,this.y,this.width,this.height);
}
}
Rectangle.prototype.hitTest=function(x,y){
return (x >= this.x) && (x <= (this.width+this.x)) &&
(y >= this.y) && (y <= (this.height+this.y));
}
var Paint = function(el) {
this.element = el;
this.shapes = [];
}
Paint.prototype.addShape = function(shape){
this.shapes.push(shape);
}
Paint.prototype.render = function(){
//clear the canvas
this.element.width = this.element.width;
var ctx = this.element.getContext('2d');
for(var i=0;i<this.shapes.length;i++){
this.shapes[i].draw(ctx);
}
}
Paint.prototype.setSelected = function(shape){
for(var i=0;i<this.shapes.length;i++){
this.shapes[i].selected = this.shapes[i]==shape;
}
this.render();
}
Paint.prototype.select = function(x,y){
for(var i=this.shapes.length-1;i>=0;i--){
if(this.shapes[i].hitTest(x,y)){
return this.shapes[i];
}
}
return null;
}
var el = document.getElementById('panel');
var paint = new Paint(el);
var rectA = new Rectangle('A',10,10,150,90,'yellow');
var rectB = new Rectangle('B',150,90,140,100,'green');
var rectC = new Rectangle('C',70,85,200,70,'rgba(0,0,0,.5)');
paint.addShape(rectA);
paint.addShape(rectB);
paint.addShape(rectC);
paint.render();
function panel_mouseUp(evt){
var p = document.getElementById('panel');
var x = evt.x - p.offsetLeft;
var y = evt.y - p.offsetTop;
var shape = paint.select(x,y);
if(shape){
alert(shape.id);
}
//console.log('selected shape :',shape);
}
function panel_mouseMove(evt){
var p = document.getElementById('panel');
var x = evt.x - p.offsetLeft;
var y = evt.y - p.offsetTop;
var shape = paint.select(x,y);
paint.setSelected(shape);
}
el.addEventListener('mouseup',panel_mouseUp);
el.addEventListener('mousemove',panel_mouseMove);
body {background:#e6e6e6;}
#panel {
border:solid thin #ccc;
background:#fff;
margin:0 auto;
display:block;
}
<canvas id="panel" width="400px" height="200px" >
</canvas>
just click or move over the shapes
Here is my code :
var ctx = document.getElementById("map").getContext("2d");
var ZeroX = 0;
var ZeroY = 0;
ctx.beginPath();
ctx.fillRect(100, 200, 100, 50); //Drawed a black rectangle
function moveMap(evt) {
var key = evt.keyCode || evt.which;
if (key == 38) { //UP
moveDirect(0, 20, false);
}
else if (key == 40) { //DOWN
moveDirect(0, 20, true);
}
else if (key == 39) { //RIGHT
moveDirect(20, 0, true);
}
else if (key == 37) { //LEFT
moveDirect(20, 0, false);
}
}
function moveDirect(X, Y, minus) {
if (minus == false) {
ZeroX -= X;
ZeroY -= Y;
}
else {
ZeroX += X;
ZeroY = Y;
}
var lol = ctx.getImageData(ZeroX, ZeroY, 3000, 3000);
ctx.clearRect(ZeroX, ZeroY, 3000, 3000);
ctx.putImageData(lol, 0, 0);
}
<body onkeypress="moveMap(event)">
<canvas id="map" width="500" height="500">Map </canvas>
</body>
If you run this snippet and click on one of the arrows on the keyboard, you'll see that the rectangle moves in the opposite
direction because I wanted to make like the screen was a camera in a
game. That's done on purpose
But if after you clicked on the arrow's oppsite (Up = Down, Left = Right,
etc), you will see that you must click two times to make it move in
the other direction. Try this with other arrows, still the same.
And if you press the same arrow many times, it's gap travelled becomes
bigger and bigger, but logically it must the same.
I want it to respond directly, not on two clicks and that the gap is always the same. Please explain in your answer why this is happennings. Thaks beforehand!
I'm making a game, and I'd like to know how to make a character move more smoothly. The character can already move, but it moves really choppy; when you click the arrow key, it instantly appears 10 pixels ahead. I'd like it to move smoothly so it doesn't just "appear" 10 pixels ahead of itself.
Here is the Code:
document.onkeydown = checkKey;
var canvas;
var ctx;
var up;
var down;
var left;
var right;
var bobX = 200;
var bobY = 200;
var bobWidth = 30;
var bobHeight = 30;
window.onload = function() {
canvas = document.getElementById("gameCanvas");
ctx = canvas.getContext("2d");
var fps = 200; // frames per second
setInterval(function() {
updateAll();
drawAll();
}, 1000/fps)
};
var drawAll = function() {
// draw background
ctx.fillStyle = "white";
ctx.fillRect(0, 0, canvas.width, canvas.height);
// draw bob
ctx.fillStyle = "red";
ctx.fillRect(bobX, bobY, bobWidth, bobHeight);
};
var updateAll = function() {
if (up == true) {
up = false;
}
if (down == true) {
bobY += 1;
down = false;
}
if (left == true) {
bobX -= 1;
left = false;
}
if (right == true) {
bobX += 1;
right = false;
}
};
function checkKey(e) {
e = e || window.event;
if (e.keyCode == '38') {
up = true;
}
else if (e.keyCode == '40') {
down = true;
}
else if (e.keyCode == '37') {
left = true;
}
else if (e.keyCode == '39') {
right = true;
}
}
I tried doing moving it by one pixel every keypress, but it moves very slowly when I do that.
Your screen has maximum refreshrate, usually 60 fps. Some screens can get up to 120fps, but that's a rather rare case.
So what is happening here:
var fps = 200; // frames per second
setInterval(function() {
updateAll();
drawAll();
}, 1000/fps)
};
The canvas gets redrawn and the position gets updated at a rate which your screen can't catch up with. You simply can't see that your character only moves 1 pixel instead of 10 pixel.
Solution would be to use requestAnimationFrame instead. Which invokes when the screen refreshes:
function animate() {
requestAnimationFrame(animate);
updateAll();
drawAll();
};
animate();
** NOTE: I've Edited the javascript below and linked to a new JSFiddle but still not getting the buttons to control the snake's movement like the arrow keys on the keyboard **
I’m trying to create a real easy snake game for project but need it to have buttons so the game will work on mobile. This is almost there except i need to have the buttons control the movement of the snake on screen:
HTML:
<div class="game-container">
<div class="container">
<div class="SplashScreen">
<h1>
Snake
</h1>
<h2>
Click To Start.
</h2>
<input class="StartButton" type="button" value="Start" />
</div>
<div class="FinishScreen" style="display:none">
<h1>
Game Over
</h1>
<p>
Your score was: <span id="score"></span>
</p>
<input class="StartButton" type="button" value="Restart" />
</div>
<canvas id="canvasArea" width="450" height="450" style="display:none;"></canvas>
</div>
<div class="button-pad">
<div class="btn-up">
<button type="submit" class="up">
<img src="http://aaronblomberg.com/sites/ez/images/btn-up.png" />
</button>
</div>
<div class="btn-right">
<button type="submit" class="right">
<img src="http://aaronblomberg.com/sites/ez/images/btn-right.png" />
</button>
</div>
<div class="btn-down">
<button type="submit" class="down">
<img src="http://aaronblomberg.com/sites/ez/images/btn-down.png" />
</button>
</div>
<div class="btn-left">
<button type="submit" class="left">
<img src="http://aaronblomberg.com/sites/ez/images/btn-left.png" />
</button>
</div>
</div>
jQuery:
( function( $ ) {
$( function() {
$(document).ready(function () {
$(".StartButton").click(function () {
$(".SplashScreen").hide();
$(".FinishScreen").hide();
$("#canvasArea").show();
init();
});
//Canvas stuff
var canvas = $("#canvasArea")[0];
var ctx = canvas.getContext("2d");
var w = $("#canvasArea").width();
var h = $("#canvasArea").height();
//Lets save the cell width in a variable for easy control
var sw = 10;
var direction;
var nd;
var food;
var score;
//Lets create the snake now
var snake_array; //an array of cells to make up the snake
function endGame() {
$("#canvasArea").hide();
$("#score").text(score);
$(".FinishScreen").show();
}
function init() {
direction = "right"; //default direction
nd = [];
create_snake();
create_food(); //Now we can see the food particle
//finally lets display the score
score = 0;
//Lets move the snake now using a timer which will trigger the paint function
//every 60ms
if (typeof game_loop != "undefined") clearInterval(game_loop);
game_loop = setInterval(paint, 60);
}
function create_snake() {
var length = 5; //Length of the snake
snake_array = []; //Empty array to start with
for (var i = length - 1; i >= 0; i--) {
//This will create a horizontal snake starting from the top left
snake_array.push({
x: i,
y: 0
});
}
}
//Lets create the food now
function create_food() {
food = {
x: Math.round(Math.random() * (w - sw) / sw),
y: Math.round(Math.random() * (h - sw) / sw),
};
//This will create a cell with x/y between 0-44
//Because there are 45(450/10) positions accross the rows and columns
}
//Lets paint the snake now
function paint() {
if (nd.length) {
direction = nd.shift();
}
//To avoid the snake trail we need to paint the BG on every frame
//Lets paint the canvas now
ctx.fillStyle = "#0056a0";
ctx.fillRect(0, 0, w, h);
ctx.strokeStyle = "#ffffff";
ctx.strokeRect(0, 0, w, h);
//The movement code for the snake to come here.
//The logic is simple
//Pop out the tail cell and place it infront of the head cell
var nx = snake_array[0].x;
var ny = snake_array[0].y;
//These were the position of the head cell.
//We will increment it to get the new head position
//Lets add proper direction based movement now
if (direction == "right") nx++;
else if (direction == "left") nx--;
else if (direction == "up") ny--;
else if (direction == "down") ny++;
//Lets add the game over clauses now
//This will restart the game if the snake hits the wall
//Lets add the code for body collision
//Now if the head of the snake bumps into its body, the game will restart
if (nx == -1 || nx == w / sw || ny == -1 || ny == h / sw || check_collision(nx, ny, snake_array)) {
//end game
return endGame();
}
//Lets write the code to make the snake eat the food
//The logic is simple
//If the new head position matches with that of the food,
//Create a new head instead of moving the tail
if (nx == food.x && ny == food.y) {
var tail = {
x: nx,
y: ny
};
score++;
//Create new food
create_food();
} else
{
var tail = snake_array.pop(); //pops out the last cell
tail.x = nx;
tail.y = ny;
}
//The snake can now eat the food.
snake_array.unshift(tail); //puts back the tail as the first cell
for (var i = 0; i < snake_array.length; i++) {
var c = snake_array[i];
//Lets paint 10px wide cells
paint_cell(c.x, c.y);
}
//Lets paint the food
paint_cell(food.x, food.y);
//Lets paint the score
var score_text = "Score: " + score;
ctx.fillStyle = "#ffffff";
ctx.fillText(score_text, 5, h - 5);
//Set the font and font size
ctx.font = '12px Arial';
//position of the fill text counter
ctx.fillText(itemCounter, 10, 10);
}
//Lets first create a generic function to paint cells
function paint_cell(x, y) {
ctx.fillStyle = "#d8d8d8";
ctx.fillRect(x * sw, y * sw, sw, sw);
}
function check_collision(x, y, array) {
//This function will check if the provided x/y coordinates exist
//in an array of cells or not
for (var i = 0; i < array.length; i++) {
if (array[i].x == x && array[i].y == y) return true;
}
return false;
}
// Lets prevent the default browser action with arrow key usage
var keys = {};
window.addEventListener("keydown",
function(e){
keys[e.keyCode] = true;
switch(e.keyCode){
case 37: case 39: case 38: case 40: // Arrow keys
case 32: e.preventDefault(); break; // Space
default: break; // do not block other keys
}
},
false);
window.addEventListener('keyup',
function(e){
keys[e.keyCode] = false;
},
false);
//Lets add the keyboard controls now
$(document).keydown(function (e) {
var key = e.which;
var td;
if (nd.length) {
var td = nd[nd.length - 1];
} else {
td = direction;
}
//We will add another clause to prevent reverse gear
if (key == "37" && td != "right") nd.push("left");
else if (key == "38" && td != "down") nd.push("up");
else if (key == "39" && td != "left") nd.push("right");
else if (key == "40" && td != "up") nd.push("down");
//The snake is now keyboard controllable
});
});
$(document).on('click', '.button-pad > button', function(e) {
if ($(this).hasClass('left-btn')) {
e = 37;
}
else if ($(this).hasClass('up-btn')) {
e = 38;
}
else if ($(this).hasClass('right-btn')) {
e = 39;
}
else if ($(this).hasClass('down-btn')) {
e = 40;
}
$.Event("keydown", {keyCode: e});
});
});
})( jQuery );
JSFiddle
http://jsfiddle.net/aaronblomberg/9w3gk3ma/3/
This is ALMOST where i need it but i need the up, down, left, and right arrow buttons to control the snake...
any help with would be greatly greatly appreciated.
You cold make something like this:
var isMobile;
checkMobile = function() {
if ($(window).width() <= 766) {
isMobile = true;
}
else {
isMobile = false;
}
}
$(window).resize(checkMobile());
$(document).ready(checkMobile());
if (isMobile) {
$('.button-pad').show();
}
else {
$('.button-pad').hide();
}
.button-pad:
$(document).on('click', '.button-pad > button', function(e) {
if ($(this).hasClass('left')) {
e = 37;
}
else if ($(this).hasClass('up')) {
e = 38;
}
else if ($(this).hasClass('right')) {
e = 39;
}
else if ($(this).hasClass('down')) {
e = 40;
}
$.Event("keydown", {keyCode: e});
}
Here is the code to add moile control to the snake game;
(If you want the whole code for the snake game please comment);
//Here is javascript;
// left key
function l() {
if(snake.dx === 0) {
snake.dx = -grid;
snake.dy = 0;
}
}
// up key
function u() {
if(snake.dy === 0) {
snake.dy = -grid;
snake.dx = 0;
}
}
// right key
function r() {
if(snake.dx === 0) {
snake.dx = grid;
snake.dy = 0;
}
}
// down key
function d() {
if(snake.dy === 0) {
snake.dy = grid;
snake.dx = 0;
}
}
Here is HTML;
<div>
<button onclick="u()" type="button" id="U">U</button>
<button onclick="l()" type="button" id="L">L</button>
<button onclick="r()" type="button" id="R">R</button>
<button onclick="d()" type="button" id="D">D</button>
</div>
Description: I simply added four buttons in the html page, created four functions in the js which will move snake as it should move in different directions and simply added them to the 'onclick' attribute of the different buttons respectively.