Я создаю игру с использованием JavaScript и холстов и хочу реализовать область просмотра, чтобы отображать более крупный игровой мир. Я уже создал элемент холста и загрузил несколько стационарных объектов и объект игрока, который можно перемещать с помощью ввода с клавиатуры.
Однако я не уверен, как реализовать окно просмотра, чтобы отображать только часть игрового мира и позволять игроку перемещаться по нему. Может ли кто-нибудь привести пример того, как реализовать окно просмотра с помощью JavaScript и холстов на основе предоставленного кода?
В частности, я ищу код, который позволит мне определить размер и положение области просмотра, а также обновить положение игрового мира в зависимости от движения игрока, чтобы игрок оставался в центре области просмотра при перемещении.
Вот мой текущий код. Обратите внимание, я очень новичок в js:
// Get the canvas element and its context
const canvas = document.createElement("canvas");
document.body.append(canvas);
const ctx = canvas.getContext("2d");
// Player coords and update method that moves the player based on input
const player = {
x: 0,
y: 0,
vel: 1,
update(){
if (key.left) this.x -= this.vel;
if (key.right) this.x += this.vel;
if (key.up) this.y -= this.vel;
if (key.down) this.y += this.vel;
}
};
// Keys pressed (Used for movement)
const key = {
left: false,
right: false,
up: false,
down: false
}
// Get Input
window.addEventListener('keydown', (e) =>
{
switch(e.code){
case "KeyW":
key["up"] = true;
break;
case "KeyS":
key["down"] = true;
break;
case "KeyA":
key["left"] = true;
break;
case "KeyD":
key["right"] = true;
break;
}
});
// Relieve Input
window.addEventListener('keyup', (e) =>
{
switch(e.code){
case "KeyW":
key["up"] = false;
break;
case "KeyS":
key["down"] = false;
break;
case "KeyA":
key["left"] = false;
break;
case "KeyD":
key["right"] = false;
break;
}
});
function update(){
ctx.clearRect(0,0,canvas.width, canvas.height);
// Draw stationary objects
ctx.fillRect(100, 100, 10, 10);
ctx.fillRect(100, 120, 10, 10);
// Draw Player and update position
player.update();
ctx.fillRect(player.x, player.y, 10, 10);
requestAnimationFrame(update);
}
update();
Я попытался сохранить контекст и перевести, а затем восстановить. Однако это не сработало, я ожидал, что игрок останется в центре экрана. Вот код, который я пробовал:
function update(){
ctx.clearRect(0,0,canvas.width, canvas.height);
// Draw stationary objects
ctx.fillRect(100, 100, 10, 10);
ctx.fillRect(100, 120, 10, 10);
// Draw Player and update position
ctx.save();
ctx.translate(player.x, player.y);
player.update();
ctx.fillRect(canvas.width/2 - 5, canvas.height/2 - 5, 10, 10);
ctx.restore();
requestAnimationFrame(update);
}
Большинство реализаций 2D-сцен в играх работают с родительским/дочерним деревом, где сцена является верхним родителем (родительское положение объекта просто делает его зависимым от положения другого объекта).
Получив это, вы можете легко перемещать область просмотра, добавляя каждый объект к одному и тому же родителю (например, сцене), содержащему все ваши объекты, и перемещая этот родитель для имитации камеры.
Вот некоторый псевдокод, чтобы получить представление о такой системе.
//Pseudo code, don't simply copy-paste it
// Make a class from which all the objects in the game will inherit; it'll take care of managing the position.
class GameObject {
position: {x: 0, y: 0}
parent: null;
children: [];
function setPosition(pX, pY) { //Sets the position within the parent
position.x = pX;
position.y = pY;
}
//This is the core of our parent/child tree
function addChild(pGameObject) {
children.push(pGameObject);
pGameObject.parent = this;
}
function removeChild() //take care of updating the parent/children vars
function toGlobalCoordinates() { //Allows us to get the global coordinates (= coordinates on the canvas)
let lParent = this.parent;
let globalPos = {x: 0, y: 0}
//Goes up the parents tree, breaks when there's no more parent thanks to the default: parent = null in the class
while(lParent) {
globalPos.x += lParent.position.x;
globalPos.y += lParent.position.y;
lParent = lParent.parent;
}
globalPos.x += this.position.x;
globalPos.y += this.position.y;
return globalPos;
}
function draw(pCtx) { //We pass the canvas ctx to be able to draw on it
//Use the global position to draw your object
let drawingPos = this.toGlobalCoordinates();
pCtx.fillRect(drawingPos.x, drawingPos.y, 100, 100);
}
}
Затем у нас есть другие классы, такие как Player, и мы должны позаботиться о рендеринге.
//extends from GameObject to benefit from the position and its methods
class Player extends GameObject {
methods() ... //Implement methods specific to the player
}
//Use the scene as the base gameobject (our top parent)
let scene = new GameObject();
let player = new Player();
let stationnaryObject1 = new GameObject()
scene.addChild(player)
scene.addChild(stationnaryObject1)
//The function called on requestAnimationFrame
update() {
//clearRect to clean the canvas then
//On key press, move the player, move the scene in the opposite direction to keep the keep focus on the player
if (keypress) {
player.setPosition(player.position.x + newX, player.position.x + newY)
scene.setPosition(scene.position.x - newX, scene.position.x - newY)
}
//Draw the objects with their new position
scene.draw(ctx);
player.draw(ctx);
}
Это более или менее базовый способ сделать камеру (т.е. переместить область просмотра в игровом мире).
Вам следует взглянуть на PixiJS, если вы ищете очень надежный способ справиться с этим и многим другим. Это очень мощная и простая в освоении JS-библиотека для обработки 2D-отображения на WebGL и холсте (больше не нужно рисовать самостоятельно!)
Чтобы узнать больше о графе/дереве сцены, о котором я говорил: https://pixijs.io/guides/basics/scene-graph.html такое же дерево использовалось, например, во Flash-играх.
TLDR: Создание камеры — это изменение позиции, к которой привязаны позиции всех объектов.
Надеюсь, вы получите удовольствие от разработки игр!
Спасибо! Это кажется очень похожим на то, как работает дерево объектов игрового движка Unity, я обязательно посмотрю на Pixi Js.