How can I move a character along a specific path using canvas?

Viewed 50

I want to move a character along a specific path. How can I achieve this? Example visualization

It is necessary for the hero to get to a point on the map, but stop at each point and, when you press the button "to the university", continues on his way. That is, when the button was pressed, it made a move.

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');
const hero = new Image();
hero.src = '../img/hero.png';
let x = 430;
let y = 425; 
hero.onload = function() {
    ctx.drawImage(hero,x,y,31,86)
}
const go = document.querySelector('.go_university');
let tur = 0;
let timer;
let direction = 0;
go.addEventListener('click',()=>{
    tur += 1;
    turn();
})

function turn(){
    ctx.clearRect(0,0,960,630);
    direction+=1;

    if(direction<=15){
        x-=2;
        y=(y-2/3);
    }else if(direction >= 15 && direction <= 28){
        x-=2;
        y=(y-2);
    }else if(direction >=28 && direction <=48){
        x-=2;
        y=(y+3/6);
    }else if(direction >= 48 && direction <=70){
        x-=2;
        y=(y+3/6);
    }

    timer = setTimeout(turn,60);
    ctx.drawImage(hero,x,y,31,86)
}

This is the code I wrote. I can't figure it out any further. How can I restore animation on click? And in general, am I doing the calculations correctly?

1 Answers

I wrote a blog post on this a while back while the JS isn't the newest the concepts are all the same. I assume you have a collection of waypoints you want to move your character towards which I define in this code under targets.

The main thing really is the pointing at a target and moving towards it

Ball.prototype.update = function () {
    // get the target x and y
    this.targetX = targets[this.target].x;
    this.targetY = targets[this.target].y;

    // We need to get the distance this time around
    var tx = this.targetX - this.x,
        ty = this.targetY - this.y,
        dist = Math.sqrt(tx * tx + ty * ty);
    
    /* 
    * we calculate a velocity for our object 
    * divide the target x and y by the distance and multiply it by our speed
    * this gives us a constant movement speed.
    */
    
    this.velX = (tx / dist) * this.speed;
    this.velY = (ty / dist) * this.speed;
    
    /* 
    * Get the direction we are facing
    * I just use -tx and -ty here because we already calculated tx and ty
    * To get the proper x and y you need to subtract the targets x and y from 
    * our objects x and y
    */
    var radians = Math.atan2(-ty,-tx);
    
    this.px = this.x - this.pointLength * Math.cos(radians);
    this.py = this.y - this.pointLength * Math.sin(radians);

    // Once we hit our target move on to the next. 
    if (dist > this.radius/2) {
        // add our velocities
        this.x += this.velX;
        this.y += this.velY;
    }else{
        this.target++;
        if(this.target == targets.length){
            this.target = 0;   
        }
    }
};

If you want to pause and wait for a click you would do that when the player reaches the target instead of automatically selecting the next target you'd do it onclick.

Full snippet included below.

var canvas = document.getElementById("canvas"),
    ctx = canvas.getContext("2d"),
    width = 500,
    height = 600,
    balls = [],
    targets = [
    {x : 450, y : 20},
    {x : 450, y : 150},
    {x : 20, y : 150},
    {x : 20, y : 250},
    {x : 450, y : 250},
    {x : 450, y : 350},
    {x : 20, y : 350},
    {x : 20, y : 450},
    {x : 450, y : 450},
    {x : 450, y : 550},
    {x : 20, y : 550},
    {x : 20, y : 20}
            ],
    started = false;

canvas.width = width;
canvas.height = height;

canvas.addEventListener("mouseenter", function (e) {
    if(!started){
        started = true;
        render();
    }
});

canvas.addEventListener("mouseleave", function (e) {
    started = false;
});

var Ball = function (x, y, radius, color) {
    this.x = x || 0;
    this.y = y || 0;
    this.radius = radius || 10;
    this.speed = 3;
    this.color = color || "rgb(255,0,0)";
    
    this.pointLength = 20;
    this.px = 0;
    this.py = 0;
    
    this.target = 0;
    this.targetX = 0;
    this.targetY = 0;
    
    this.velX = 0;
    this.velY = 0;
}

Ball.prototype.update = function () {
    // get the target x and y
    this.targetX = targets[this.target].x;
    this.targetY = targets[this.target].y;

    // We need to get the distance this time around
    var tx = this.targetX - this.x,
        ty = this.targetY - this.y,
        dist = Math.sqrt(tx * tx + ty * ty);
    
    /* 
    * we calculate a velocity for our object this time around
    * divide the target x and y by the distance and multiply it by our speed
    * this gives us a constant movement speed.
    */
    
    this.velX = (tx / dist) * this.speed;
    this.velY = (ty / dist) * this.speed;
    
    /* 
    * Get the direction we are facing
    * I just use -tx and -ty here because we already calculated tx and ty
    * To get the proper x and y you need to subtract the targets x and y from 
    * our objects x and y
    */
    var radians = Math.atan2(-ty,-tx);
    
    this.px = this.x - this.pointLength * Math.cos(radians);
    this.py = this.y - this.pointLength * Math.sin(radians);

    // Once we hit our target move on to the next. 
    if (dist > this.radius/2) {
        // add our velocities
        this.x += this.velX;
        this.y += this.velY;
    }else{
        this.target++;
        if(this.target == targets.length){
            this.target = 0;   
        }
    }
};

Ball.prototype.render = function () {
    ctx.fillStyle = this.color;
    ctx.beginPath();
    // draw our circle with x and y being the center
    ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
    ctx.closePath();
    ctx.fill();
    
    ctx.strokeStyle = "rgb(0,0,255)";
    ctx.beginPath();
    ctx.moveTo(this.x, this.y);
    ctx.lineTo(this.px, this.py);
    ctx.closePath();
    ctx.stroke();
};

for(var i = 0; i < 20; i++){
    balls.push(new Ball(20 - i * 30, 20, 10));
}

function render() {
    ctx.clearRect(0, 0, width, height);
    balls.forEach(function(el){
        el.update();
        el.render();
    });
    if(started){
        requestAnimationFrame(render);
    }
}

render();
canvas {
    border:1px solid black;
}
<canvas id="canvas"></canvas>

Related