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, 2020 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, 2020 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, 2020 at 9:41

1 Answer 1

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, 2020 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, 2020 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, 2020 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, 2020 at 9:22

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .