我在画布上渲染了一个对象。我想让这个对象沿着预设的路径循环移动。以下是我的代码:
// Canvas Element
var canvas = null;
// Canvas Draw
var ctx = null;
// Static Globals
var tileSize = 16,
mapW = 10,
mapH = 10;
// Instances of entities
var entities = [
// A single entity that starts at tile 28, and uses the setPath() function
{
id: 0,
tile: 28,
xy: tileToCoords(28),
width: 16,
height: 24,
speedX: 0,
speedY: 0,
logic: {
func: 'setPath',
// These are the parameters that go into the setPath() function
data: [0, ['down', 'up', 'left', 'right'], tileToCoords(28), 0]
},
dir: {up:false, down:false, left:false, right:false}
}
];
// Array for tile data
var map = [];
window.onload = function(){
// Populate the map array with a blank map and 4 walls
testMap();
canvas = document.getElementById('save');
ctx = canvas.getContext("2d");
// Add all the entities to the map array and start their behavior
for(var i = 0; i < entities.length; ++i){
map[entities[i].tile].render.object = entities[i].id;
if(entities[i].logic){
window[entities[i].logic.func].apply(null, entities[i].logic.data);
}
}
drawGame(map);
window.requestAnimationFrame(function(){
mainLoop();
});
};
function drawGame(map){
ctx.clearRect(0, 0, canvas.width, canvas.height);
// We save all the entity data for later so the background colors don't get rendered on top
var tileObjData = [];
for(var y = 0; y < mapH; ++y){
for(var x = 0; x < mapW; ++x){
var currentPos = ((y*mapW)+x);
ctx.fillStyle = map[currentPos].render.base;
ctx.fillRect(x*tileSize, y*tileSize, tileSize, tileSize);
var thisObj = map[currentPos].render.object;
if(thisObj !== false){
thisObj = entities[thisObj];
var originX = thisObj.xy.x;
var originY = thisObj.xy.y;
tileObjData.push(
{
id: thisObj.id,
originX: originX,
originY: originY,
width: thisObj.width,
height: thisObj.height,
}
);
}
}
}
// Draw all the entities after the background tiles are drawn
for(var i = 0; i < tileObjData.length; ++i){
drawEntity(tileObjData[i].id, tileObjData[i].originX, tileObjData[i].originY, tileObjData[i].width, tileObjData[i].height);
}
}
// Draws the entity data
function drawEntity(id, posX, posY, sizeX, sizeY){
var offX = posX + entities[id].speedX;
var offY = posY + entities[id].speedY;
ctx.fillStyle = '#00F';
ctx.fillRect(offX, offY + sizeX - sizeY, sizeX, sizeY);
entities[id].xy.x = offX;
entities[id].xy.y = offY;
}
// Redraws the canvas with the browser framerate
function mainLoop(){
drawGame(map);
for(var i = 0; i < entities.length; ++i){
animateMove(i, entities[i].dir.up, entities[i].dir.down, entities[i].dir.left, entities[i].dir.right);
}
window.requestAnimationFrame(function(){
mainLoop();
});
}
// Sets the speed, direction, and collision detection of an entity
function animateMove(id, up, down, left, right){
var prevTile = entities[id].tile;
if(up){
var topLeft = {x: entities[id].xy.x, y: entities[id].xy.y};
var topRight = {x: entities[id].xy.x + entities[id].width - 1, y: entities[id].xy.y};
if(!map[coordsToTile(topLeft.x, topLeft.y - 1)].state.passable || !map[coordsToTile(topRight.x, topRight.y - 1)].state.passable){
entities[id].speedY = 0;
}
else{
entities[id].speedY = -1;
}
}
else if(down){
var bottomLeft = {x: entities[id].xy.x, y: entities[id].xy.y + entities[id].width - 1};
var bottomRight = {x: entities[id].xy.x + entities[id].width - 1, y: entities[id].xy.y + entities[id].width - 1};
if(!map[coordsToTile(bottomLeft.x, bottomLeft.y + 1)].state.passable || !map[coordsToTile(bottomRight.x, bottomRight.y + 1)].state.passable){
entities[id].speedY = 0;
}
else{
entities[id].speedY = 1;
}
}
else{
entities[id].speedY = 0;
}
if(left){
var bottomLeft = {x: entities[id].xy.x, y: entities[id].xy.y + entities[id].width - 1};
var topLeft = {x: entities[id].xy.x, y: entities[id].xy.y};
if(!map[coordsToTile(bottomLeft.x - 1, bottomLeft.y)].state.passable || !map[coordsToTile(topLeft.x - 1, topLeft.y)].state.passable){
entities[id].speedX = 0;
}
else{
entities[id].speedX = -1;
}
}
else if(right){
var bottomRight = {x: entities[id].xy.x + entities[id].width - 1, y: entities[id].xy.y + entities[id].width - 1};
var topRight = {x: entities[id].xy.x + entities[id].width - 1, y: entities[id].xy.y};
if(!map[coordsToTile(bottomRight.x + 1, bottomRight.y)].state.passable || !map[coordsToTile(topRight.x + 1, topRight.y)].state.passable){
entities[id].speedX = 0;
}
else{
entities[id].speedX = 1;
}
}
else{
entities[id].speedX = 0;
}
entities[id].tile = coordsToTile(entities[id].xy.x + (entities[id].width / 2), entities[id].xy.y + (tileSize / 2));
map[entities[id].tile].render.object = id;
if(prevTile !== entities[id].tile){
map[prevTile].render.object = false;
}
}
//////////////////////////////////////
// THIS IS WHERE I'M HAVING TROUBLE //
//////////////////////////////////////
// A function that can be used by an entity to move along a set path
// id = The id of the entity using this function
// path = An array of strings that determine the direction of movement for a single tile
// originPoint = Coordinates of the previous tile this entity was at. This variable seems to be where problems happen with this logic. It should get reset for every tile length moved, but it only gets reset once currently.
// step = The current index of the path array
function setPath(id, path, originPoint, step){
// Determine if the entity has travelled one tile from the origin
var destX = Math.abs(entities[id].xy.x - originPoint.x);
var destY = Math.abs(entities[id].xy.y - originPoint.y);
if(destX >= tileSize || destY >= tileSize){
// Go to the next step in the path array
step = step + 1;
if(step >= path.length){
step = 0;
}
// Reset the origin to the current tile coordinates
originPoint = entities[id].xy;
}
// Set the direction based on the current index of the path array
switch(path[step]) {
case 'up':
entities[id].dir.up = true;
entities[id].dir.down = false;
entities[id].dir.left = false;
entities[id].dir.right = false;
break;
case 'down':
entities[id].dir.up = false;
entities[id].dir.down = true;
entities[id].dir.left = false;
entities[id].dir.right = false;
break;
case 'left':
entities[id].dir.up = false;
entities[id].dir.down = false;
entities[id].dir.left = true;
entities[id].dir.right = false;
break;
case 'right':
entities[id].dir.up = false;
entities[id].dir.down = false;
entities[id].dir.left = false;
entities[id].dir.right = true;
break;
};
window.requestAnimationFrame(function(){
setPath(id, path, originPoint, step);
});
}
// Take a tile index and return x,y coordinates
function tileToCoords(tile){
var yIndex = Math.floor(tile / mapW);
var xIndex = tile - (yIndex * mapW);
var y = yIndex * tileSize;
var x = xIndex * tileSize;
return {x:x, y:y};
}
// Take x,y coordinates and return a tile index
function coordsToTile(x, y){
var tile = ((Math.floor(y / tileSize)) * mapW) + (Math.floor(x / tileSize));
return tile;
}
// Generate a map array with a blank map and 4 walls
function testMap(){
for(var i = 0; i < (mapH * mapW); ++i){
// Edges
if (
// top
i < mapW ||
// left
(i % mapW) == 0 ||
// right
((i + 1) % mapW) == 0 ||
// bottom
i > ((mapW * mapH) - mapW)
) {
map.push(
{
id: i,
render: {
base: '#D35',
object: false,
sprite: false
},
state: {
passable: false
}
},
);
}
else{
// Grass
map.push(
{
id: i,
render: {
base: '#0C3',
object: false,
sprite: false
},
state: {
passable: true
}
},
);
}
}
}
<!DOCTYPE html>
<html>
<head>
<style>
body{
background-color: #000;
display: flex;
align-items: center;
justify-content: center;
color: #FFF;
font-size: 18px;
padding: 0;
margin: 0;
}
main{
width: 100%;
max-width: 800px;
margin: 10px auto;
display: flex;
align-items: flex-start;
justify-content: center;
flex-wrap: wrap;
}
.game{
width: 1000px;
height: 1000px;
position: relative;
}
canvas{
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-crisp-edges;
image-rendering: pixelated;
image-rendering: crisp-edges;
}
.game canvas{
position: absolute;
top: 0;
left: 0;
width: 800px;
height: 800px;
}
</style>
</head>
<body>
<main>
<div class="game">
<canvas id="save" width="200" height="200" style="z-index: 1;"></canvas>
</div>
</main>
</body>
</html>
问题出在setPath()
函数上,更具体地说,我认为是与originPoint
变量有关。思路是setPath()
每个
['down','up','left','right']
,它应该仅向下移动16像素,向上移动16像素,向左移动16像素,然后向右移动16像素。 - jadletilesize
。我希望目标地图坐标由path
数组确定。感谢你花时间帮助我。 - jadleoriginPoint = entities[id].xy;
这行代码是在做什么呢?我觉得它并没有实现你想要的功能。 - Helder SepulvedaoriginPoint
存储对象访问的上一个瓷砖的坐标。因此,当对象到达新的瓷砖(已经行驶了16px的一格)时,originPoint
会更新为新的坐标,并且path
数组的下一步就会发生。 - jadle