- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我创建了一个旋转函数来旋转我绘制的三 Angular 形,函数参数是我想要旋转形状的度数。旋转功能仍然不会旋转。
我尝试调用该函数在旋转函数中以不同的线绘制三 Angular 形。
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
//central coordinates, width and height of triangle
let ship_center = {x: 450, y: 300};
let ship_width = 20;
let ship_height = 20;
//coordinates of the points of the vertices for my triangle
let ship_points = [
//top vertex
{x: 450 - ship_width/2, y: ship_center.y + ship_height/2},
//bottom right vertex
{x: 450 + ship_width/2, y: ship_center.y + ship_height/2},
//bottom left vertex
{x: ship_center.x, y: ship_center.y - ship_height/2}
];
function drawRect(x, y, width, height, color){
ctx.rect(x, y, width, height);
ctx.fillStyle = color;
ctx.fill();
}
//bottom left vertices, bottom right verices and top vertices
//as parameters in drawTriangle
function drawTriangle(bottom_left, bottom_right, top, color){
ctx.beginPath();
ctx.moveTo(top.x, top.y);
ctx.lineTo(bottom_left.x, bottom_left.y);
ctx.lineTo(bottom_right.x, bottom_right.y);
ctx.lineTo(top.x, top.y);
ctx.strokeStyle = color;
ctx.stroke();
ctx.closePath();
}
//rotation function
function rotate(angle){
ctx.save();
//draw the triangle
drawTriangle(ship_points[2], ship_points[1], ship_points[0],
"white");
ctx.translate(ship_center.x, ship_center.y);
ctx.rotate(Math.PI/180 * angle);
ctx.restore();
}
function game(){
drawRect(0, 0, 900, 600, "black");
//rotate 10 degrees
rotate(10);
}
let gameLoop = setInterval(game, 10);
<canvas id="canvas">
no support
</canvas>
预期结果:三 Angular 形向左旋转 10 度。实际结果:没有任何旋转的正常三 Angular 形。
最佳答案
也许比您要求的要多,但您在代码中犯了一些常见错误,这些错误将对最终代码产生负面影响,并剥夺编写游戏的一些乐趣
当你有一个旋转,移动,也许缩放(缩放)形状时,最好定义以其自身原点(局部空间)为中心的形状(如评论中所指出的),以便将其转换为出现在如果您在世界坐标中创建对象, Canvas (世界空间)不需要将其移动到本地空间并返回的复杂性。
不要在每次渲染时创建船舶路径,而是使用 Path2D定义形状。这可以通过将计算移至启动来避免创建路径的一些计算开销。
Canvas 变换的自然方向(向前)是沿着 X 轴。当构建在世界空间中移动的对象时,最好让前点沿着同一轴。你让船沿着 y 轴的负方向指向上方。
ctx.closePath
认为 ctx.closePath 类似于 ctx.beginPath
是一个非常常见的错误。 。 closePath
与 beginPath
无关,它更像 lineTo
并从最后一个路径点到前一个 创建一条附加线移动到
代码将船舶定义为 2D 路径,其前端沿 x 轴指向。
const shipShape = (() => {
const width = 20, height = 20;
const ship = new Path2D()
ship.lineTo(-width / 2, -height / 2);
ship.lineTo(width / 2, 0);
ship.lineTo(-width / 2, height / 2);
ship.closePath();
return ship;
})();
您正在努力编写过于复杂的代码。随着游戏的发展,这种复杂性将开始变得越来越难以进行更改和管理错误。始终努力使其尽可能简单。
有很多方法可以转换要渲染的对象。最常见的方法就是像你所做的那样。然而,这种方法需要多次 GPU 状态更改(CPU 和 GPU 之间的数据交换)。状态更改可能非常慢(尤其是在低端设备上)
代码的下一个片段标记状态更改
ctx.save();
// the ctx.stroke in the following function is a state change
drawTriangle(ship_points[2], ship_points[1], ship_points[0],"white");
// All 3 of the following calls are state changes.
ctx.translate(ship_center.x, ship_center.y);
ctx.rotate(Math.PI/180 * angle);
ctx.restore(); // Depending on the saved state this can be very time expensive
最糟糕的状态变化是 ctx.restore
这高度依赖于保存的状态以及保存和恢复之间对状态所做的更改。如果您需要性能代码,您应该不惜一切代价避免使用保存和恢复。
下一个示例将以尽可能少的状态更改次数渲染 2D 形状,并以最快的方式使用 2D API 渲染转换后的内容。然而,它确实保持状态不变,因此您必须在后续渲染中意识到这一点。根据需要完全定义每个状态比使用保存和恢复更有效。
请注意,我添加了比例,因为您可能需要一些时间。
function strokeShape(shape, pos, rotate = 0, scale = 1, style = ctx.strokeStyle) {
const xAx = Math.cos(rotate) * scale;
const xAy = Math.sin(rotate) * scale;
ctx.setTransform(xAx, xAy, -xAy, xAx, pos.x, pos.y); // set rotate scale and position
// in one state change
ctx.strokeStyle = style;
ctx.stroke(shape);
}
要画船只需要线条
strokeShape(shipShape, {x:450, y:300}, rotate, 1, "white");
将它们放在一起,我们得到以下结果。
requestAnimationFrame
执行动画(切勿使用 setInterval
)Path2D
的通用函数注意到您关于运动的第二个问题。当问题得到解答时,我想稍微扩展一下这个演示,以给出一些关于如何移动船只和其他一些游戏相关内容的提示。点击启动向上推力,左右转动。
var started = false;
canvas.addEventListener("click",() => {
if (!started) {
requestAnimationFrame(updateFrame);
started = true;
}
})
const ctx = canvas.getContext("2d", {aplha:false});// aplha:false to avoid unneeded composition
ctx.font = "16px arial";
ctx.textAlign = "center";
fillBackground();
ctx.fillStyle = "white"
ctx.fillText("Click to Start", ctx.canvas.width / 2, ctx.canvas.height / 2);
document.addEventListener("keydown", keyboardEvent);
document.addEventListener("keyup", keyboardEvent);
const keys = {ArrowUp: false, ArrowLeft: false, ArrowRight: false}
function keyboardEvent(event) {
if(keys[event.code] !== undefined) {
event.preventDefault();
keys[event.code] = event.type === "keydown";
}
}
const width = 20, height = 20;
const TURN_RATE = 0.01; // in radians
const MAX_TURN_RATE = 0.1; // in radians
const REACTOR_WINDUP_RATE = 0.01; // in power units per frame
const REACTOR_MAX_POWER = 0.1; // in pixels per frame (frame = 1/60th sec)
const SPACE_QUANTUM_FLUX = 0.015; // drains ship moment per frame
const DEFLUXING_CONVERTER = 0.8; // How dirty the thruster is
const SHIP_HULL = [-width*(1/3), -height/2, width*(2/3), 0, -width*(1/3), height/2,"close"];
const SHIP_PORT = [width*(1/6), -height/8, width*(1/3), 0, width*(1/6), height/8,"close"];
const thrustParticlePool = [];
const thrustParticle = {
get pos() { return {x:0, y:0} },
get vel() { return {x:0, y:0} },
shape: createPath([-0.5,0,0.5,0]),
style: "#FFF",
rotate: 0,
pool: thrustParticlePool,
update() {
this.pos.x += this.vel.x;
this.pos.y += this.vel.y;
this.vel.x *= 0.996;
this.vel.y *= 0.996;
this.life -= 1;
},
init(x,y,direction, speed) {
const offCenter = Math.random()**2 * (Math.random() < 0.5 ? -1 : 1);
const offCenterA = Math.random()**2 * (Math.random() < 0.5 ? -1 : 1);
speed += speed * offCenterA;
speed **= 2.5;
this.pos.x = x + Math.cos(direction) * width * (2/3) - Math.sin(direction) * height * (1/6) * offCenter;
this.pos.y = y + Math.sin(direction) * width * (2/3) + Math.cos(direction) * height * (1/6) * offCenter;
direction += direction * 0.1 * offCenter;
this.rotate = direction;
this.vel.x = Math.cos(direction) * speed;
this.vel.y = Math.sin(direction) * speed;
this.life = 100;
},
};
const particles = Object.assign([],{
add(type,...args) {
var p;
if(type.pool.length) {
p = type.pool.pop();
} else {
p = Object.assign({}, type);
}
p.init(...args);
this.push(p);
},
updateDraw() {
var i = 0
while(i < this.length) {
const p = this[i];
p.update();
if (p.life <= 0) {
this.splice(i--,1)[0];
if (p.pool) { p.pool.push(p) }
} else {
strokeShape(p.shape, p.pos, p.rotate, 1, p.style);
}
i++;
}
}
});
function createPath(...paths) {
var i, path = new Path2D;
for(const points of paths) {
i = 0;
path.moveTo(points[i++],points[i++])
while (i < points.length -1) { path.lineTo(points[i++],points[i++]) }
points[i] === "close" && path.closePath();
}
return path;
}
const ship = {
shapes: {
normal: createPath(SHIP_HULL, SHIP_PORT),
thrustingA: createPath(SHIP_HULL, SHIP_PORT,
[-width*(1/3), -height/4, -width*(1/3)-height/4,0, -width*(1/3), height/4]
),
thrustingB: createPath(SHIP_HULL, SHIP_PORT,
[-width*(1/3), -height/3.5, -width*(1/3)-height/2.4,0, -width*(1/3), height/3.5]
),
},
shape: null,
rotate: 0, // point left to right along x axis
deltaRotate: 0,
pos: {x : 200, y: 100},
vel: {x : 0, y: 0},
power: 0,
style: "#FFF", // named colours take about 10% longer to set than Hex colours
update() {
if (keys.ArrowUp) {
this.shape = this.shapes.thrustingA === this.shape ? this.shapes.thrustingB : this.shapes.thrustingA;
this.power = this.power < REACTOR_MAX_POWER ? this.power + REACTOR_WINDUP_RATE : REACTOR_MAX_POWER;
if (Math.random() < DEFLUXING_CONVERTER) {
particles.add(
thrustParticle,
this.pos.x, this.pos.y,
this.rotate + Math.PI,
this.power * 8,
);
}
} else {
this.shape = this.shapes.normal;
this.power = 0;
}
var dr = this.deltaRotate;
dr *= 0.95;
dr = keys.ArrowLeft ? dr - TURN_RATE : dr;
dr = keys.ArrowRight ? dr + TURN_RATE : dr;
dr = Math.abs(dr) > MAX_TURN_RATE ? MAX_TURN_RATE * Math.sign(dr) : dr;
this.rotate += (this.deltaRotate = dr);
this.vel.x += Math.cos(this.rotate) * this.power;
this.vel.y += Math.sin(this.rotate) * this.power;
const speed = (this.vel.x * this.vel.x + this.vel.y * this.vel.y)**4;
if (speed > 0.0) {
this.vel.x = this.vel.x * (speed / (speed * (1+SPACE_QUANTUM_FLUX)));
this.vel.y = this.vel.y * (speed / (speed * (1+SPACE_QUANTUM_FLUX)));
}
this.pos.x += this.vel.x;
this.pos.y += this.vel.y;
this.pos.x = (this.pos.x + ctx.canvas.width * 2) % ctx.canvas.width;
this.pos.y = (this.pos.y + ctx.canvas.height * 2) % ctx.canvas.height;
},
draw() {
strokeShape(ship.shape, ship.pos, ship.rotate, 1, ship.style);
}
};
function strokeShape(shape, pos, rotate = 0, scale = 1, style = ctx.strokeStyle) {
const xAx = Math.cos(rotate) * scale; // direction and size of the top of a
const xAy = Math.sin(rotate) * scale; // single pixel
ctx.setTransform(xAx, xAy, -xAy, xAx, pos.x, pos.y); // one state change
ctx.strokeStyle = style;
ctx.stroke(shape);
}
function fillBackground() {
ctx.fillStyle = "#000";
ctx.setTransform(1,0,0,1,0,0); //ensure that the GPU Transform state is correct
ctx.fillRect(0, 0, ctx.canvas.width, ctx.canvas.height);
}
function updateFrame(time) {
fillBackground();
ship.update();
particles.updateDraw();
ship.draw();
requestAnimationFrame(updateFrame);
}
<canvas id="canvas" width="400" height="200"></canvas>
关于javascript - 在纯 JavaScript 中旋转三 Angular 形,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58554114/
可以抛出异常的函数可以有[pure]属性吗? 最佳答案 根据 https://msdn.microsoft.com/en-us/library/system.diagnostics.contracts
我使用的是纯 css 推送导航。它工作得很好,但是我不知道如何在单击导航链接时隐藏菜单。您必须手动单击菜单图标才能使菜单返回隐藏状态。但是,当单击链接并且站点跳转到某个部分时,我希望菜单自动滑入隐藏状
我正在尝试让纯 CSS 下拉菜单正常工作。它在很大程度上确实有效,除了其他内容似乎显示出来但我不知道为什么。 http://jsfiddle.net/uQveP/4/ 有人可以告诉我我做错了什么吗?
这个问题在这里已经有了答案: What is a "callback" in C and how are they implemented? (9 个回答) 关闭 8 年前。 我正在以这种方式实现回
我想在不使用 Javascript 或任何其他语言的情况下,使用 HTML 和 CSS 创建一个 Page Back Button。我想用纯 HTML 和 CSS 来完成。 我进行了搜索,但每次代码中
我对序言很陌生。据我所知,Pure Prolog 仅限于 Horn 子句。 这是一个非常简单的序言程序 - % student( Snr , FirstName , LastName ,
我想在加载数据时对容器使用以下加载指示器。 问题是, slider 具有固定的宽度和高度(300 像素和 300 像素),但我希望它能够动态适应容器。当我尝试添加宽度时:140px;和高度:140px
当内容超过可用宽度时,我需要启用滚动阴影。这是我试图用纯 css(没有 JS)来实现的。我遇到了很多文章,可以使用 css 多背景和背景附件来实现。如果内容是文本类型,则可以使用下面的 jsfilld
我正在编写一个上古卷轴在线插件,它由一个名为 Havok Script 的轻微修改的 Lua 5.1 引擎支持。 .这个Lua环境不允许访问os , io , package , debug模块或任何
我自己尝试过将 Arduino 库编译成他们自己的独立库并链接到 Eclipse 中的一个项目,但在此过程中遇到了一些问题。 是否有关于如何启动和运行的体面指南?我一直很难在网上找到一个真正有效的..
我在这里遇到了一些麻烦。我正在尝试使用本地存储创建一个待办事项列表,但我唯一要做的就是将列表项添加到本地存储并删除 所有项目 从本地存储中删除,但我无法从列表中删除单个 SELECTED 项目。有人可
我的问题很简单。考虑以下 CodePen .是否有可能仅使用 css 就可以获得相同的结果?换句话说,如果不使用 javascrip 如何做到这一点?非常感谢! Nachos are
我正在使用没有 jquery 的 angularjs,并尝试创建滚动事件监听器。 尝试过这种方法: $rootScope.$watch(function() { return $windo
我正在尝试使用纯 webgl 创建虚线。我知道这已经有一个问题,也许我很笨,但我不知道如何让它发挥作用。我理解这个概念,但我不知道如何在着色器中获取沿路径的距离。以前的答案有以下行: varying
我正在尝试用纯 JavaScript 制作工具提示,显示在 hover .就像 Stack Overflow 中将鼠标悬停在配置文件名称上的一个 div显示。 我尝试使用 onmouseover ,
我想要通过 AJAX 将监听器添加到新元素的想法: 例如,现在我有 hello world 我为每个 添加了一个监听器,但是当我通过 AJAX 加载新元素时,它没有监听器;我不完全确定问题是什么。
如果我错误地提出了这个问题,或者之前已经有人问过并回答过这个问题,我提前表示歉意。我的搜索发现了类似的基于 JQuery 和/或静态日期的问答,我正在寻找具有动态日期的纯 JavaScript 解决方
在 Real World Haskell, Chapter 28, Software transactional memory ,开发了一个并发的网络链接检查器。它获取网页中的所有链接,并使用 HEA
我正在尝试取消 jQuery-fy 一个聪明的 piece of code ,但有点太聪明了。 目标是simple 。将图像从桌面拖动到浏览器。 在这次 unjQueryfication 过程中,我发
如何重新创建 jQuery end() $('#id') .find('.class') .css('font',f) .end() .find('.seven') .css(b,'red') 我有什
我是一名优秀的程序员,十分优秀!