1
\$\begingroup\$

I want to make a snake game. In a classic game the snake moves in 4 directions, I want the player to move in all directions by mouse input, similar to slither.io

Right now the tail section changes its position after the head. If I increase the speed, the sections move away from each other. How can I make the sections not move away from each other and have a configurable distance between them?

Code snippet:

var canvas = document.getElementById("canvas");
var ctx = canvas.getContext("2d");

// Change speed of movement
var speed = 5.0

var mouseMovementX = 0;
var mouseMovementY = 0;

document.addEventListener("mousemove", function(event){
	mouseMovementX = event.clientX;
	mouseMovementY = event.clientY;
});	


var key = {
	a: false,
	s: false
}

function keyboard(){
	//$(window).keydown(function(event){
	document.addEventListener('keydown', (event) => {
		if( event.which == 83 ){
			key.s = true
		} 
		if( event.which == 65 ){
			key.a = true
		}	
	});
document.addEventListener('keyup', (event) => {
	//$(window).keyup(function(event){
		if( event.which == 83 ){
			key.s = false
		} 
		if( event.which == 65 ){
			key.a = false
		}	
	});
}


function drawCircle(x, y, rotation){
	ctx.moveTo(x, y);
	ctx.beginPath();
	ctx.arc(x,y,12,0,Math.PI*2,false);
	ctx.closePath();
	ctx.fill();
	ctx.strokeStyle = "#ccc";
	ctx.stroke();
} 

function radToDeg(value){
	return ( value * 180 ) / Math.PI;
}

function degToRad(value){
	return ( value * Math.PI ) / 180;
}

// Clamp
function clamp(Ax,Ay,Bx,By,L){			
	var Vx = Bx - Ax;
	var Vy = By - Ay;
	
	var lmax = L;
	var l = Math.sqrt( Math.pow(Vx,2) + Math.pow(Vy,2) );
	
	var lclamp = Math.min( l, lmax );
	
	var vclampX = ( Vx / l ) * lclamp 
	var vclampY = ( Vy / l ) * lclamp 
	
	var bclampX = Ax + vclampX
	var bclampY = Ay + vclampY

	return { x: bclampX, y: bclampY  }
}


// Update tail
function updateTail(i){
	for( i2 = app.object[i].body.length-1; i2 >= 0; i2--){
		if( i2 == 0 ){						
			var r = clamp(
				app.object[i].head.x,
				app.object[i].head.y,
				app.object[i].body[i2].x,
				app.object[i].body[i2].y,
				app.object[i].sectionDistance
			);	
					
			app.object[i].body[i2].x = r.x 
			app.object[i].body[i2].y = r.y
		}else{
			var r = clamp(
				app.object[i].body[i2-1].x,
				app.object[i].body[i2-1].y,
				app.object[i].body[i2].x,
				app.object[i].body[i2].y,
				app.object[i].sectionDistance
			);	
					
			app.object[i].body[i2].x = r.x
			app.object[i].body[i2].y = r.y
		}
	}
}

// Update previous positions
function updatePrevPosition(i){
	app.object[i].head.xPrev = app.object[i].head.x
	app.object[i].head.yPrev = app.object[i].head.y
	app.object[i].head.anglePrev = app.object[i].head.angle
}

var app = {
	object: []
}

// Create snake object
var options = {
	size:0,
	length:20,
	width: 24,
	height: 24,
	sectionDistance: 10,
	head: {
		x: 200,
		y: 200,
		xPrev:0,
		yPrev:0,
		angle: 0
	},
	body: []
}

app.object.push(options)

for( i = 0; i <  app.object[0].length;i++){
	app.object[0].body.push({ 
		x: (6*i)+200,
		y: (6*i)+200,
		angle: 0,
		color:0
	})
}


// Draw
var numberOfObjects = app.object.length;

function draw(){
	for( i = 0; i < numberOfObjects; i++ ){
		if( i == 0 ){
			for( i2 = app.object[i].body.length-1; i2 >= 0; i2-- ){	
				drawCircle((canvas.width/2)+( app.object[i].body[i2].x - app.object[i].head.x) ,(canvas.height/2)+( app.object[i].body[i2].y - app.object[i].head.y ),app.object[i].body[i2].angle)
			}
			
			drawCircle((canvas.width/2),(canvas.height/2),app.object[i].head.angle)
		}else{
			var tempX = ( app.object[i].head.x - app.object[0].head.x ) + ( canvas.width / 2 )
			var tempY = ( app.object[i].head.y - app.object[0].head.y ) + ( canvas.height / 2 )

			drawCircle(tempX,tempY,app.object[i].head.angle)
		}
	}
}

// Update positions
function update(){

	if( key.a == true ){
		speed -= 0.1;
		if(speed < 0.1 ){ speed = 0.1 }
		console.log(speed)
	}
	if( key.s == true ){
		speed += 0.1;
		if( speed > 10 ){ speed = 10 }
		console.log(speed)
	}						
							
							
							
	angle = ( Math.atan2(mouseMovementX - ( canvas.width / 2 ) , mouseMovementY - ( canvas.height / 2 )) * -1 );
	
	newX = Math.sin(angle) * speed
	newY = Math.cos(angle) * speed

	app.object[0].head.angle = angle 

	app.object[0].head.x -= newX 
	app.object[0].head.y += newY 
	
	updateTail(0)
	updatePrevPosition(0)
		
	draw();
}

keyboard();
// Animate
function animate(){
	ctx.clearRect(0,0,canvas.width,canvas.height);
	requestAnimationFrame(animate);
	update();  
}
animate();
html, body{
	width:100%;
	height:100%;
	padding:0;
	margin:0;
	background-color:blue;
}
canvas{
	display:block;
	background-color:white;
}
<canvas id="canvas" width="500" height="300"></canvas>

\$\endgroup\$
3
  • 1
    \$\begingroup\$ This type of 'open ended' question would benefit very heavily from a video/gif, or a picture of what is happening right now, and an illustration of what you want to happen. \$\endgroup\$
    – Natalo77
    Jun 8 '20 at 9:06
  • \$\begingroup\$ Presumably you've checked through previous Q&A in the snake tag for starting points here? Or looked up a snake tutorial for JavaScript? \$\endgroup\$
    – DMGregory
    Jun 8 '20 at 9:32
  • \$\begingroup\$ I went through the results on Google and Stack Overflow, I didn't find an explanations of how to make the distance between sections configurable that works with variable speed \$\endgroup\$ Jun 8 '20 at 9:41
0
\$\begingroup\$

1)Take a vector from segment A to segment B

2)Calculate vector length

3)Check if length exceeds maximum, clamp if it does

4)Normalize vector and multiply it by clamped length

5)Set position of segment B to position of A + clamped vector

Desmos graph: https://www.desmos.com/calculator/eilpcyx5gp

EDIT: Since segments depend on each other's position during clamping, you should go from head to tail, not from tail to head. Rewrite your loop accordingly:

for( i2 = 0; i2 < app.object[i].body.length; i2++){

\$\endgroup\$
4
  • \$\begingroup\$ I made it work, but after increasing the speed of movement the distance between sections still increases. I updated the snippet to see the effect, use keys "A" and "S" to increase/decrease the speed \$\endgroup\$ Jun 9 '20 at 6:57
  • \$\begingroup\$ @Freeman123 just ran the demo and it seems to me like you apply clamping to only two segments, algorithm should be applied to each segment \$\endgroup\$
    – Ocelot
    Jun 9 '20 at 8:23
  • \$\begingroup\$ The clamping is applied to all segments. In "updateTail()" function, the second part of the condition is where the positions for the rest of the segmens are calculated. \$\endgroup\$ Jun 9 '20 at 8:46
  • \$\begingroup\$ @Freeman123 I really shouldn't have debugged your code but I updated the answer with the result. \$\endgroup\$
    – Ocelot
    Jun 9 '20 at 9:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.