我如何给这艘宇宙飞船加速? [英] How do I give this spaceship acceleration?

查看:133
本文介绍了我如何给这艘宇宙飞船加速?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有一个非常小的类似于小行星的游戏片段,我正在使用没有Canvas的DOM。当按下箭头键时,我的船移动相当顺畅但是当箭头键按下一段较长的时间时,我将如何使船加速(速度和旋转)?

Have a very small snippet of an asteroids-like game I'm working on using only the DOM without Canvas. I have the "ship" moving pretty smoothly when arrow keys are pressed but how would I go about making the ship accelerate ( in speed and rotation ) when an arrow key is held down for a longer length of time?

window.onkeyup = function( e ) {
  var kc = e.keyCode;
  e.preventDefault();

  if ( kc === 37 ) Keys.left = false;
  else if ( kc === 38 ) Keys.up = false;
  else if ( kc === 39 ) Keys.right = false;
  else if ( kc === 40 ) Keys.down = false;
};

function update() {
  if ( Keys.up ) {
    document.querySelector( 'div' ).style.transform += 'translateY( -1px )';
  }
  else if ( Keys.down ) {
    document.querySelector( 'div' ).style.transform += 'translateY( 1px )';
  }

  if ( Keys.left ) {
    document.querySelector( 'div' ).style.transform += 'rotate( -1deg )';
  }
  else if ( Keys.right ) { 
    document.querySelector( 'div' ).style.transform += 'rotate( 1deg )';
  }
  
  requestAnimationFrame( update );
}
requestAnimationFrame( update );

@import url( "https://fonts.googleapis.com/css?family=Nunito" );

html, body {
  height: 100%;
  margin: 0;
  padding: 0;
  font-family: "Nunito", sans-serif;
  font-size: 2rem;
}

body {
  display: flex;
  justify-content: center;
  align-items: center;
}

b {
  display: block;
  transform: rotate( 180deg );
}

<div>
  <b>v</b>
</div>

<script>
  var Keys = {
    up: false,
    down: false,
    left: false,
    right: false
  }

  window.onkeydown = function( e ) {
    var kc = e.keyCode;
    e.preventDefault();

    if ( kc === 37 ) Keys.left = true;
    else if ( kc === 38 ) Keys.up = true;
    else if ( kc === 39 ) Keys.right = true;
    else if ( kc === 40 ) Keys.down = true;
  };
</script>

使用箭头用于控制代码段的键。

推荐答案

已更新。由于另一个类似的问题得到了基于此前版本答案的答案,我已将答案更改为更好的答案。

Updated. As another similar question had a answer based on this previous version answer I have changed the answer to the better answer.

有很多方法可以将运动应用到小行星类型的游戏中。这个答案显示了最基本的方法,然后举例说明了产生不同感觉的基本方法的变化。这个答案还简要概述了如何使用矩阵设置CSS转换(2D)

There are so many ways to apply movement to a asteroids type game. This answer shows the most basic method and then gives an example showing variations on the basic methods to produce different feels. This answer also give a brief overview of how to set the CSS transformation using matrix (2D)

在最基本的情况下,您有一个代表位置或旋转的某个组成部分的数字。要移动你添加常量 x + = 1; 一次移动 x 一个单位你不会添加的控件然后停止。

At the most basic you have a number that represents some component of the position or rotation. To move you add a constant x += 1; move in x one unit at a time when you let go of the control you don`t add and you stop.

但事情不会那样移动,它们会加速。所以你创建了一个保持速度的第二个值(以前是 x + = 1 中的一个),并将其称为 dx (delta X)。当你得到输入时,你将速度增加一些小的 dx + = 0.01 ,以便及时的速度逐渐增加。

But things don't move like that, they accelerate. So you create a second value that holds the speed (formerly the one from x += 1) and call it dx (delta X). When you get input you increase the speed by some small amount dx += 0.01 so that in time the speed increases gradually.

但问题是你控制的时间越长,你走的越快,当你放开控制时,船就会继续前进(这对于太空来说是正常的,但游戏中很痛)所以你需要限制速度并逐渐降低到零。您可以通过将缩放应用于每帧的增量X值来实现。 dx * = 0.99;

But the problem is the longer you hold the controls the faster you go, and when you let go of the controls the ship keeps going (which is all normal for space but a pain in a game) so you need to limit the speed and bring it gradually back down to zero. You can do that by applying a scale to the delta X value each frame. dx *= 0.99;

因此,你有基本的加速度,阻力和速度限制值

Thus you have the basic acceleration, drag, and speed limited value

x += dx;
dx *= 0.99;
if(input){ dx += 0.01);

对x,y和角度都这样做。如果输入是方向性的,你需要使用x,y的向量,如下所示。

Do that for both the x, y and angle. Where input is directional you need to use vectors for x, y as follows.

x += dx;
y += dy;
angle += dAngle;
dx *= 0.99;
dy *= 0.99;
dAngle *= 0.99;
if(turnLeft){
     dAngle += 0.01;
}
if(turnRight){
     dAngle -= 0.01;
}
if(inputMove){ 
    dx += Math.cos(angle) * 0.01;
    dy += Math.sin(angle) * 0.01;
}

这是最基本的太空游戏运动。

That is the most basic space game movement.

通过matrix命令设置CSS变换最简单。例如,设置默认转换 element.style.transform =matrix(1,0,0,1,0,0);

Setting the CSS transform is easiest apply via the matrix command. eg setting default transform element.style.transform = "matrix(1,0,0,1,0,0)";

六个值通常命名为 a,b,c,d,e '矩阵(a,b,c,d,e,f)'或 m11,m12, m21,m22,m31,m32
水平缩放,水平倾斜,垂直倾斜,垂直缩放,水平移动,垂直移动并表示3 x 3的缩短版本2D矩阵。

The six values often named a,b,c,d,e 'matrix(a,b,c,d,e,f)' or m11, m12, m21, m22, m31, m32 or horizontal scaling, horizontal skewing, vertical skewing, vertical scaling, horizontal moving, vertical moving and represent a shortened version of the 3 by 3 2D matrix.

我发现大多数关于这个矩阵如何工作以及为什么不经常使用的混淆部分是由于变量的命名。我更喜欢描述为x轴x,x轴y,y轴x,y轴y,原点x,原点y,并简单描述x和y轴的方向和比例以及原点在CSS像素坐标中的位置。

I find that the majority of confusion about how this matrix works and why it is not often used is in part due to the naming of the variables. I prefer the description as x axis x, x axis y, y axis x, y axis y, origin x, origin y and simply describe the direction and scale of the x and y axis and the position of the origin all in CSS pixel coordinates.

下图说明了矩阵。红色框是一个已经旋转45度(Math.PI / 4弧度)的元素,其原点移动到CSS像素协调16,16。

The following image illustrates the matrix. The red box is a element that has been rotated 45 deg (Math.PI / 4 radians) and its origin moved to CSS pixel coordinated 16,16.

图像网格显示CSS像素。右边的网格显示矩阵的缩放视图,显示X轴向量(a,b)=(cos(45),sin(45)),Y轴向量(c,d)=(cos(45 + 90), sin(45 + 90))和Origin(e,f)=(16,16)

Image Grid shows CSS pixels. The right grid shows a zoomed view of the matrix showing the X Axis vector (a,b) = (cos(45), sin(45)), Y Axis vector (c,d) = (cos(45 + 90), sin(45 + 90)) and the Origin (e,f) = (16, 16)

因此,我有一个元素的值角度,位置(x,y),比例(x,y)。然后我们按如下方式创建矩阵

Thus is I have the values for an element in terms of angle, position (x,y), scale (x,y). We then create the matrix as follows

var scale = { x : 1, y : 1 };
var pos = {x : 16, y : 16 };
var angle = Math.PI / 4; // 45 deg
var a,b,c,d,e,f; // the matrix arguments
// the x axis and x scale
a = Math.cos(angle) * scale.x;
b = Math.sin(angle) * scale.x;
// the y axis which is at 90 degree clockwise of the x axis
// and the y scale
c = -Math.sin(angle) * scale.y;
d = Math.cos(angle) * scale.y;
// and the origin
e = pos.x;
f = pos.y;

element.style.transform = "matrix("+[a,b,c,d,e,f].join(",")+")";

由于大部分时间我们都没有偏斜变换并使用统一的比例,我们可以缩短代码。我更喜欢使用预定义的数组来帮助保持GC命中率低。

As most of the time we dont skew the transform and use a uniform scale we can shorten the code. I prefer to use a predefined array to help keep GC hits low.

const preDefinedMatrix = [1,0,0,1,0,0]; // define at start

// element is the element to set the CSS transform on.
// x,y the position of the elements local origin
// scale the scale of the element
// angle the angle in radians
function setElementTransform (element, x, y, scale, angle) {
    var m = preDefinedMatrix;
    m[3] = m[0] = Math.cos(angle) * scale;
    m[2] = -(m[1] = Math.sin(angle) * scale);
    m[4] = x;
    m[5] = y;
    element.style.transform = "matrix("+m.join(",")+")";
}

我在演示中使用了稍微不同的功能。 ship.updatePos 并使用 ship.pos ship.displayAngle 设置相对于包含元素原点的转换(上,左)

I use a slightly different function in the demo. ship.updatePos and uses the ship.pos and ship.displayAngle to set the transformation relative to the containing elements origin (top,left)

注意那个3D矩阵虽然有点更复杂(包括投影)非常类似于2D矩阵,它将x,y和z轴描述为3个向量,每个向量具有3个标量(x,y,z),y轴与x轴成90度并且z轴与x轴和y轴成90度,并且可以用x点y轴的叉积求得。每个轴的长度是刻度,原点是点坐标(x,y,z)。

Note that the 3D matrix though a little more complex (includes the projection) is very similar to the 2D matrix, it describes the x,y, and z axis as 3 vectors each with 3 scalars (x,y,z) with the y axis at 90 deg to the x axis and the z axis at 90 deg to both the x and y axis and can be found with the cross product of the x dot y axis. The length of each axis is the scale and the origin is a point coordinate (x,y,z).

演示显示 4 5种变体。使用键盘1,2,3,4,5选择一艘船(它将变为红色)并使用箭头键飞行。你去的基本向上箭头,你左转。

The demo shows 4 5 variants. Use the keyboard 1,2,3,4,5 to select a ship (it will turn red) and use the arrow keys to fly. Basic up arrow you go, left right you turn.

每艘船的数学都在对象 ship.controls

The maths for each ship is in the object ship.controls

requestAnimationFrame(mainLoop);
const keys = {
    ArrowUp : false,
    ArrowLeft : false,
    ArrowRight : false,
    Digit1 : false,
    Digit2 : false,
    Digit3 : false,
    Digit4 : false,
    Digit5 : false,
    event(e){ 
        if(keys[e.code] !== undefined){ 
            keys[e.code] = event.type === "keydown" ;
            e.preventDefault();
        } 
    },
}
addEventListener("keyup",keys.event);
addEventListener("keydown",keys.event);
const ships = {
    items : [],
    controling : 0,
    add(ship){ this.items.push(ship) },
    update(){
        var i;
        
        for(i = 0; i < this.items.length; i++){
            if(keys["Digit" + (i+1)]){
                if(this.controling !== -1){
                    this.items[this.controling].element.style.color = "green";
                    this.items[this.controling].hasControl = false;
                }
                this.controling = i;
                this.items[i].element.style.color = "red";
                this.items[i].hasControl = true;
            }
            this.items[i].updateUserIO();
            this.items[i].updatePos();
        }
    }
    
}
const ship = {
    element : null,
    hasControl : false,
    speed : 0,
    speedC : 0,  // chase value for speed limit mode
    speedR : 0,  // real value (real as in actual speed)
    angle : 0,
    angleC : 0,  // as above
    angleR : 0,
    engSpeed : 0,
    engSpeedC : 0,
    engSpeedR : 0,
    displayAngle : 0, // the display angle
    deltaAngle : 0,
    matrix : null,  // matrix to create when instantiated 
    pos : null,     // position of ship to create when instantiated 
    delta : null,   // movement of ship to create when instantiated 
    checkInView(){
        var bounds = this.element.getBoundingClientRect();
        if(Math.max(bounds.right,bounds.left) < 0 && this.delta.x < 0){
            this.pos.x = innerWidth;
        }else if(Math.min(bounds.right,bounds.left) > innerWidth  && this.delta.x > 0){
            this.pos.x = 0;
        }
        if(Math.max(bounds.top,bounds.bottom) < 0  && this.delta.y < 0){
            this.pos.y = innerHeight;
        }else if( Math.min(bounds.top,bounds.bottom) > innerHeight  && this.delta.y > 0){
            this.pos.y = 0;
        }
        
    },
    controls : {
        oldSchool(){
            if(this.hasControl){
                if(keys.ArrowUp){
                    this.delta.x += Math.cos(this.angle) * 0.1;
                    this.delta.y += Math.sin(this.angle) * 0.1;
                }
                if(keys.ArrowLeft){
                    this.deltaAngle -= 0.001;
                }
                if(keys.ArrowRight){
                    this.deltaAngle += 0.001;
                }
            }
            this.pos.x += this.delta.x;
            this.pos.y += this.delta.y;
            this.angle += this.deltaAngle;
            this.displayAngle = this.angle;
            this.delta.x *= 0.995;
            this.delta.y *= 0.995;
            this.deltaAngle *= 0.995;            
        },
        oldSchoolDrag(){
            if(this.hasControl){
                if(keys.ArrowUp){
                    this.delta.x += Math.cos(this.angle) * 0.5;
                    this.delta.y += Math.sin(this.angle) * 0.5;
                }
                if(keys.ArrowLeft){
                    this.deltaAngle -= 0.01;
                }
                if(keys.ArrowRight){
                    this.deltaAngle += 0.01;
                }
            }
            this.pos.x += this.delta.x;
            this.pos.y += this.delta.y;
            this.angle += this.deltaAngle;
            this.delta.x *= 0.95;
            this.delta.y *= 0.95;
            this.deltaAngle *= 0.9;
            this.displayAngle = this.angle;
        },
        speedster(){
            if(this.hasControl){
                
                if(keys.ArrowUp){
                    this.speed += 0.02;
                }
                if(keys.ArrowLeft){
                    this.deltaAngle -= 0.01;
                }
                if(keys.ArrowRight){
                    this.deltaAngle += 0.01;
                }
            }
            this.speed *= 0.99;
            this.deltaAngle *= 0.9;
            this.angle += this.deltaAngle;
            this.delta.x += Math.cos(this.angle) * this.speed;
            this.delta.y += Math.sin(this.angle) * this.speed;
            this.delta.x *= 0.95;
            this.delta.y *= 0.95;
            this.pos.x += this.delta.x;
            this.pos.y += this.delta.y;
            this.displayAngle = this.angle;
        },
        engineRev(){  // this one has a 3 control. Engine speed then affects acceleration. 
            if(this.hasControl){
                if(keys.ArrowUp){
                    this.engSpeed = 3
                }else{
                    this.engSpeed *= 0.9;
                }
                if(keys.ArrowLeft){
                    this.angle -= 0.1;
                }
                if(keys.ArrowRight){
                    this.angle += 0.1;
                }
            }else{
                this.engSpeed *= 0.9;
            }
            this.engSpeedC += (this.engSpeed- this.engSpeedR) * 0.05;
            this.engSpeedC *= 0.1;
            this.engSpeedR += this.engSpeedC;
            this.speedC += (this.engSpeedR - this.speedR) * 0.1;
            this.speedC *= 0.4;
            this.speedR += this.speedC;
            this.angleC += (this.angle - this.angleR) * 0.1;
            this.angleC *= 0.4;
            this.angleR += this.angleC;
            this.delta.x += Math.cos(this.angleR) * this.speedR * 0.1; // 0.1 reducing this as easier to manage speeds when values near pixel size and not 0.00umpteen0001
            this.delta.y += Math.sin(this.angleR) * this.speedR * 0.1;
            this.delta.x *= 0.99;
            this.delta.y *= 0.99;
            this.pos.x += this.delta.x;
            this.pos.y += this.delta.y;
            this.displayAngle = this.angleR;
        },
        speedLimiter(){
            if(this.hasControl){
    
                if(keys.ArrowUp){
                    this.speed = 15;
                }else{
                    this.speed = 0;
                }
                if(keys.ArrowLeft){
                    this.angle -= 0.1;
                }
                if(keys.ArrowRight){
                    this.angle += 0.1;
                }
            }else{
                this.speed = 0;
            }
            this.speedC += (this.speed - this.speedR) * 0.1;
            this.speedC *= 0.4;
            this.speedR += this.speedC;
            this.angleC += (this.angle - this.angleR) * 0.1;
            this.angleC *= 0.4;
            this.angleR += this.angleC;
            this.delta.x = Math.cos(this.angleR) * this.speedR;
            this.delta.y = Math.sin(this.angleR) * this.speedR;
            this.pos.x += this.delta.x;
            this.pos.y += this.delta.y;
            this.displayAngle = this.angleR;
        }
    },
    updateUserIO(){
    },
    updatePos(){
        this.checkInView();
        var m = this.matrix;
        m[3] = m[0] = Math.cos(this.displayAngle);
        m[2] = -(m[1] = Math.sin(this.displayAngle));
        m[4] = this.pos.x;
        m[5] = this.pos.y;
        this.element.style.transform = `matrix(${m.join(",")})`;
    },
    create(shape,container,xOff,yourRide){  // shape is a string
        this.element = document.createElement("div")
        this.element.style.position = "absolute";
        this.element.style.top = this.element.style.left = "0px";
        this.element.style.fontSize = "24px";
        this.element.textContent = shape;
        this.element.style.color  = "green";
        this.element.style.zIndex  = 100;

        container.appendChild(this.element);
        this.matrix = [1,0,0,1,0,0];
        this.pos = { x : innerWidth / 2 + innerWidth * xOff, y : innerHeight / 2 };
        this.delta = { x : 0, y : 0};
        this.updateUserIO = this.controls[yourRide];
        return this;
    }
}
var contain = document.createElement("div");
contain.style.position = "absolute";
contain.style.top = contain.style.left = "0px";
contain.style.width = contain.style.height = "100%";
contain.style.overflow = "hidden";
document.body.appendChild(contain);
window.focus();




ships.add(Object.assign({},ship).create("=Scl>",contain,-0.4,"oldSchool"));
ships.add(Object.assign({},ship).create("=Drg>",contain,-0.25,"oldSchoolDrag"));
ships.add(Object.assign({},ship).create("=Fast>",contain,-0.1,"speedster"));
ships.add(Object.assign({},ship).create("=Nimble>",contain,0.05,"speedLimiter"));
ships.add(Object.assign({},ship).create("=Rev>",contain,0.2,"engineRev"));
function mainLoop(){
    ships.update();
    requestAnimationFrame(mainLoop);
}

body {
  font-family : verdana;
  background : black;
  color : #0F0;
 }

Use keyboard keys 1,2,3, 4, or 5 to select a ship. Use arrow keys to fly the ship.

还有很多其他变种和方法。我喜欢使用二阶导数(一阶导数 dx / dt(dt是时间),从x + = dx,第二个de / dt用于发动机功率),它模拟发动机提升功率并降低功率可以给人一种非常好的感觉。基本上它的

There are many other variants and ways. I like the using a second derivative (first derivative dx/dt (dt is time) from x += dx, second de/dt for engine power) that simulates an engine ramping up power and winding down which can give a very nice feel. Basicly its

 x += dx;
 dx += de;
 dx *= 0.999;
 de *= 0.99;
 if(input){ de += 0.01 }

什么适合你的游戏是由你决定,你不必遵守规则,所以尝试不同的价值观和方法,直到你开心。

What is suited for your game is up to you, you don't have to follow the rules so try out different values and methods till you are happy.

这篇关于我如何给这艘宇宙飞船加速?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆