javascript - 我如何使用键盘输入更改精灵我正在使用 vanilla JS
问题描述
所以我一直在测试 HTML 画布。我试图让精灵改变键盘输入。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<canvas id='Game' width='200' height='200' style='border: 2px solid #000000;'></canvas>
<script>
window.onload = function(){
var Game = document.getElementById('Game');
var context = Game.getContext('2d')
var room = new Image();
var lx = 0;
var ly = 0;
var li = 0;
var lo = 0;
var lwidth = 100;
var lheight = 100;
room.onload = function(){
context.drawImage(room,lx,ly,lwidth,lheight,li,lo,200,200);
}
room.src = 'https://i.ibb.co/D7fL7yN/Room.png';
var sprite = new Image();
var cx = 0;
var cy = 125;
var sy = 0;
var sx = 0;
var swidth = 35;
var sheight = 34;
sprite.onload = function(){
context.drawImage(sprite,sx,sy,swidth,sheight,cx,cy,50,50);
}
sprite.src = 'https://i.ibb.co/7VhjqPr/John-Sheet.png';
}
</script>
</body>
</html>
我一直在寻找如何使用键盘输入更改 SX,以便我的角色更改精灵。你能帮助我吗?最好有一个代码示例!
解决方案
跟踪键盘状态。
您可以创建一个保存键盘状态的对象,特别是您有兴趣响应的键。使用"keydown"
和KeyboardEvent在事件触发时更新键盘状态。使用 KeyboardEvent 属性代码来确定哪个键正在更改。不要使用已贬值且非标准的keyCode"keyup"
您还希望防止键的默认行为。例如,防止箭头键滚动页面。这是通过调用事件 preventDefault函数来完成的
const keys = {
ArrowRight: false,
ArrowLeft: false,
ArrowUp: false,
ArrowDown: false,
}
addEventListener("keydown", keyEvent);
addEventListener("keyup", keyEvent);
function keyEvent(event) {
if (keys[event.code] !== undefined) {
keys[event.code] = event.type === "keydown";
event.preventDefault();
}
}
然后在游戏中你只需要检查键盘状态
if (keys.ArrowRight) { moveRight() }
if (keys.ArrowLeft) { moveLeft() }
// and so on
在下面的演示中,键绑定到游戏动作,这意味着使用什么键以及使用多少键与动作无关。也是通过配置设置的,因此可以在不更改游戏代码的情况下更改键绑定。您还可以绑定其他输入,如示例
动画
要制作动画,您应该使用计时器函数requestAnimationFrame,因为它专门设计用于提供最佳动画效果。它将调用您的渲染函数,您可以将渲染函数视为循环,即每次在动画时间前进时调用。
把它放在一起
下面的演示使用上述(修改后的)方法来获取键盘输入并通过动画帧请求渲染场景。
它还使用一些技术(简单版本)来帮助您的游戏成为更好的产品。
- 封装
player
为对象 - 通过保持当前渲染函数保持游戏状态
currentRenderState
- 具有配置
config
,因此所有重要值都在一个位置,并且可以(从 JSON 文件)加载以轻松更改游戏而无需更改代码。 - 具有可配置的键盘绑定,注意多个键可以绑定到一个游戏动作。例如,移动是通过 WASD 或箭头键。
- 所有文本都是可配置的(使其与语言无关)
- 将 2D 上下文传递给所有渲染代码。
- 将游戏与渲染分开。这使得将游戏移植到低端或高端设备更容易,甚至可以将其移动到 ctx 替换为 coms 并且可以广播游戏的服务器。游戏不仅仅改变它的渲染方式
var currentRenderState = getFocus; // current game state
const config = {
player: {
start: {x: 100, y:100},
speed: 2,
imageURL: "https://i.stack.imgur.com/C7qq2.png?s=64&g=1",
},
keys: { // link key code to game action
up: ["ArrowUp", "KeyW"],
down: ["ArrowDown", "KeyS"],
left: ["ArrowLeft", "KeyA"],
right: ["ArrowRight", "KeyD"],
},
touchableTime: 140, // in ms. Set to 0 or remove to deactivate
text: {
focus: "Click canvas to get focus",
loading: "Just a moment still loading media!",
instruct: "Use arrow keys or WASD to move",
}
};
requestAnimationFrame(mainLoop); // request first frame
const ctx = gameCanvas.getContext("2d");
const w = gameCanvas.width, h = gameCanvas.height;
const player = {
image: (()=> {
const img = new Image;
img.src = config.player.imageURL;
img.addEventListener("load", () => player.size = img.width, {once: true});
return img;
})(),
x: config.player.start.x,
y: config.player.start.y,
size: 0,
speed: config.player.speed,
direction: 0,
update() {
var oldX = this.x, oldY = this.y;
if (actions.left) { this.x -= this.speed }
if (actions.right) { this.x += this.speed }
if (actions.up) { this.y -= this.speed }
if (actions.down) { this.y += this.speed }
if (this.x < 0) { this.x = 0 }
else if (this.x > w - this.size) { this.x = w - this.size }
if (this.y < 0) { this.y = 0 }
else if (this.y > h - this.size) { this.y = h - this.size }
const mx = this.x - oldX, my = this.y - oldY;
if (mx !== 0 || my !== 0) { this.direction = Math.atan2(my, mx) }
},
draw(ctx) {
if (ctx) {
ctx.setTransform(1, 0, 0, 1, this.x + this.size / 2, this.y + this.size / 2);
ctx.rotate(this.direction + Math.PI / 2); // rotate 90 deg as image points up
ctx.drawImage(this.image,-this.size / 2, -this.size / 2, this.size, this.size);
}
}
}
function drawText(ctx, text, size, color) {
if (ctx) {
ctx.fillStyle = color;
ctx.font = size + "px Arial";
ctx.textAlign = "center";
ctx.fillText(text, w / 2, h * (1/4));
}
}
function getFocus(ctx) {
drawText(ctx, config.text.focus, 24, "black");
}
function drawScene(ctx) {
if (!player.size === 0) {
drawText(ctx, config.text.loading, 16, "blue")
actions.hasInput = false; // ensure instruction are up when ready
} else {
if (!actions.hasInput) { drawText(ctx, config.text.instruct, 16, "blue") }
player.update();
player.draw(ctx);
}
}
function mainLoop() {
ctx.setTransform(1, 0, 0, 1, 0, 0);
ctx.clearRect(0, 0, w, h);
currentRenderState(ctx);
requestAnimationFrame(mainLoop); // request next frame
}
// keys holds action name for each named key. eg for up action ArrowUp: "up", KeyW: "up",
const keys = Object.entries(config.keys)
.reduce((keys, [action,codes]) =>
codes.reduce((keys, code) => (keys[code] = action, keys), keys), {});
// actions are set true when key down. NOTE first up key for action cancels action
const actions = Object.keys(config.keys)
.reduce((actions,action) => (actions[action] = false, actions),{});
addEventListener("keydown", keyEvent);
addEventListener("keyup", keyEvent);
function keyEvent(event) {
if (keys[event.code] !== undefined) {
actions[keys[event.code]] = event.type === "keydown";
event.preventDefault();
actions.hasInput = true;
}
}
if (config.touchableTime) {
const actionTimers = {};
touchable.addEventListener("click", (e) => {
if (e.target.dataset.action) {
actions[e.target.dataset.action] = true;
clearTimeout(actionTimers[e.target.dataset.action]);
actionTimers[e.target.dataset.action] = setTimeout(() => actions[e.target.dataset.action] = false, config.touchableTime);
actions.hasInput=true;
if (currentRenderState !== drawScene) {
window.focus();
currentRenderState = drawScene;
}
}
});
} else {
touchable.classList.add("hide");
}
gameCanvas.addEventListener("click", () => currentRenderState = drawScene, {once: true});
canvas {border: 1px solid black}
#game {
width:402px;
height:182px;
font-size: 24px;
user-select: none;
}
.left {
position: absolute;
top: 160px;
left: 10px;
cursor: pointer;
}
.right {
position: absolute;
top: 160px;
left: 355px;
cursor: pointer;
}
#touchable span:hover {color: red}
.hide { display: none }
<div id="game">
<canvas id="gameCanvas" width="400" height="180"></canvas>
<div id="touchable">
<div class="left">
<span data-action="up">▲</span>
<span data-action="down">▼</span>
</div>
<div class="right">
<span data-action="left">◄</span>
<span data-action="right">►</span>
</div>
</div>
</div>
推荐阅读
- r - ggplot2:组间加权散点图
- r - 如何在 R 的 lm 中将“权重”列名作为变量传递?
- asp.net-mvc - 问题在 azure devops 中为 mvc 应用程序设置构建(找不到类型或命名空间名称“mvc”)
- docker - 无法连接到从另一个容器作为容器运行的烧瓶,尽管我可以 ping
- rubygems - 如何解决 fastlane v 2.162.0 的 gem 安装冲突
- java - 在线程“main”java.lang.ArrayIndexOutOfBoundsException 错误中获取异常,并且一直试图找出解决方案
- redis - Redis 独立服务器正在动态更改配置
- c++ - 使用链表调用堆栈的默认构造函数获取错误 C2760
- javascript - 在子道具上分配 onClick 时出错
- augmented-reality - 如果我们需要为室内导航创建一个锚点“网格”,最佳实践是什么?