我需要在加载页面时让这个球随机移动,但只能以 15 x 15 的移动,例如,现在球在 (80,80) 它可以随机移动到 (95,80) , (80,95), (65,80) 或 (80,65),它需要每 15 px 保持移动,但不是那么快,它需要在那里停留片刻,然后再次移动
老实说我不知道该怎么办,我一直被卡住了,但还没有弄清楚如何去做,请帮我一个简单的方法
"use strict";
let ctx;
function setup() {
let canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");
ball(80,80);
}
function ball(x,y) {
ctx.save();
ctx.translate(x, y);
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(50, 50, 15, 0, 2 * Math.PI); // head
ctx.fill();
}
function moveBall(){
ball(x,y);
}
function moveRandom() {
Math.floor(Math.random()*8)*30 + 15
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Run</title>
<script src="script.js"></script>
</head>
<body onload="setup()">
<h1>Run</h1>
<canvas id="myCanvas" height="400" width="400" style="border: 1px solid black"></canvas>
</body>
</html>
答案 0 :(得分:1)
它不是很清楚,你想要什么样的运动,但是下面的片段应该给出基本的想法,你可以进一步细化它以获得所需的输出:
"use strict";
let ctx, canvas;
function setup() {
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");
ball(80,80);
}
function ball(x,y) {
//first clear the canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.save();
//ctx.translate(x, y);
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(x, y, 15, 0, 2 * Math.PI); // head
ctx.fill();
}
function moveBall(){
var x = moveRandom();
var y = moveRandom();
console.log("moving to: ", x,y);
ball(x, y);
}
function moveRandom() {
//canvas width and height is same in your case, so multiplied by one to get both x and y. This will give x, y values within canvas.
return Math.floor(Math.random()*canvas.width);
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Run</title>
<script src="script.js"></script>
</head>
<body onload="setup()" onclick="moveBall()">
<h1>Run</h1>
<canvas id="myCanvas" height="400" width="400" style="border: 1px solid black"></canvas>
</body>
</html>
运动是身体的 onclick
,但可以通过简单地从所需事件调用 moveBall
函数来改变,例如mousemove
、canvas onclick
等。或者,如果您想要基于时间的移动,请使用 setInterval
或 requestAnimationFrame
和 setTimeout
来调用 moveBall
。>
答案 1 :(得分:1)
让我们从创建一个 Vector 类开始。它会让事情变得更容易。我们当然是在 2D 中工作,因此我们的向量将是 2D。所以我们创建了一个带有 x
和 y
的构造函数:
class Vector {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
除非我们有一些操作,否则这些向量并不是真正的向量。即加法和标量积。所以让我们添加它们:
add(other) {
return new Vector(this.x + other.x, this.y + other.y);
}
如您所见,加法只是将向量成员相加。
对于 calar 乘积,我们只需将成员乘以一个因子:
scaled(factor) {
return new Vector(this.x * factor, this.y * factor);
}
要进行减法,我们可以做a.add(b.sacled(-1))
,但使用差分法很方便:
diff(other) {
return new Vector(this.x - other.x, this.y - other.y);
}
我们想要一个向量的“范数”或长度,它只是毕达哥拉斯的:
length() {
return Math.sqrt(this.x * this.x + this.y * this.y);
}
现在,我们有了 Vector 类,让我们谈谈游戏周期。 什么?你说这不是游戏?好吧,嘘,我们正在写它。
在网络中,我们希望将 requestAnimationFrame
用于您的游戏周期。我们将在初始化 (setup
) 期间将其称为 1,并且我们将使其自身调用。我们将使用 performace.now()
来打发时间:
function tick(newTime){
// ...
requestAnimationFrame(tick);
}
function setup() {
// ...
time = performance.now();
tick(time);
}
我们需要知道自上一帧过去了多长时间。因此,在外部作用域中声明 time
,以跟踪最后一次,我们像这样计算 delta
:
function tick(newTime){
let delta = (newTime - time) / 1000.0;
time = newTime;
// ...
requestAnimationFrame(tick);
}
请注意,我除以 1000.0
,这样我才能以秒而不是毫秒为单位获得值。
游戏周期将接受输入(在本例中为无)、更新状态(移动球)和输出(绘制到画布)。
更新状态
所以,我们需要一个状态。让我们用一个向量来表示球的位置。类似地,它要去的位置、速度、速度等......
事实上我们会有:
let position = new Vector(80, 80); // Pixels
let target = new Vector(80, 80); // Pixels
let direction = new Vector(0, 0); // Pixels
let speed = 15.0; // Pixels per second
let step = 15.0; // Pixels
现在,球在 delta
秒内移动的距离是:
let distanceToCover = delta * speed;
我们可以像这样更新位置:
position = position.add(direction.scaled(distanceToCover));
这里我们假设 direction
是一个单位向量。将其缩放 distanceToCover
为我们提供了球在 delta
秒内移动的偏移量。我们将其添加到当前位置以获得更新的位置。
但是球向哪个方向移动?好吧,一旦球到达目标,我们就随机选择一个方向……啊,我们需要检查是否到达目标!
if (position.diff(target).length() < distanceToCover) {
// ...
}
在那里我们计算从位置到目标的距离,看看它是否小于球将覆盖的距离。您可以将其视为检查我们是否即将超过目标。
请注意,我们不能完全依赖与目标匹配的位置。一方面,实际上球是离散地改变位置的(除非它每帧都这样做),所以它可能不会击中目标。另一方面,它无论如何也不会因为浮点错误。
好的,现在我们需要一个随机的方向。这很简单,我们使用 Math.random()
:
let angle = (Math.random() * Math.PI * 2.0) - Math.PI;
direction = new Vector(Math.cos(angle), Math.sin(angle));
在那里我们选择一个从 -PI 到 PI 弧度范围内的角度,然后我们使用三角法从中创建一个单位向量。
我们还需要更新目标位置:
target = position.add(direction.scaled(step));
目标位置是当前位置加上我们计算的方向,按步长缩放。
我发现了一个错误。好古老的隧道。所以我将测量行进的距离而不是到目标的距离。所以我需要起始位置而不是目标。
这是固定代码:
if (position.diff(start).length() + distanceToCover > step) {
// ...
start = position;
}
哦,还有一件事,如果我将 start
初始化为与 position
相同的值,然后它永远不会选择另一个 start
,那么该条件将是错误的。因此,start
必须初始化为远离 position
。
我知道还有其他事情需要改进以提高“正确性”。特别是将位置重置为计算的目标,并考虑我们超过多少以从运动中移除时间。
但是,请记住计算机图形学的第一定律:如果看起来正确,那就正确 -Flecher Dunn & Ian Parberry
输出
简单,清空画布,画球:
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall(position);
这是工作代码:
"use strict";
class Vector {
constructor(x, y) {
this.x = x;
this.y = y;
}
add(other) {
return new Vector(this.x + other.x, this.y + other.y);
}
diff(other) {
return new Vector(this.x - other.x, this.y - other.y);
}
scaled(factor) {
return new Vector(this.x * factor, this.y * factor);
}
length() {
return Math.sqrt(this.x * this.x + this.y * this.y);
}
}
let ctx;
let canvas;
let time;
let position = new Vector(80, 80); // Pixels
let start = new Vector(Infinity, Infinity); // Pixels
let direction = new Vector(0, 0); // Pixels
let speed = 15.0; // Pixels per second
let step = 15.0; // Pixels
function tick(newTime){
let delta = (newTime - time) / 1000.0;
time = newTime;
let distanceToCover = delta * speed;
if (position.diff(start).length() + distanceToCover > step) {
let angle = (Math.random() * Math.PI * 2.0) - Math.PI;
direction = new Vector(Math.cos(angle), Math.sin(angle));
start = position;
}
position = position.add(direction.scaled(distanceToCover));
ctx.clearRect(0, 0, canvas.width, canvas.height);
drawBall(position);
requestAnimationFrame(tick);
}
function setup() {
canvas = document.getElementById("myCanvas");
ctx = canvas.getContext("2d");
time = performance.now();
tick(time);
}
function drawBall(position) {
ctx.save();
{
ctx.translate(position.x, position.y);
ctx.fillStyle = "red";
ctx.beginPath();
ctx.arc(50, 50, 15, 0, 2 * Math.PI); // head
ctx.fill();
}
ctx.restore();
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Run</title>
<script src="script.js"></script>
</head>
<body onload="setup()">
<h1>Run</h1>
<canvas id="myCanvas" height="400" width="400" style="border: 1px solid black"></canvas>
</body>
</html>