编程语言
首页 > 编程语言> > javascript – 如何提供这种太空飞船加速?

javascript – 如何提供这种太空飞船加速?

作者:互联网

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

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>

使用箭头键控制代码段.

解决方法:

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

转换,加速,拖动和火箭飞船.

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

基础

在最基本的情况下,您有一个代表位置或旋转的某个组成部分的数字.移动你添加一个常数x = 1;当你放开你不添加的控制并停止时,一次移动一个单位.

但事情并没有那样移动,而是加速了.因此,您创建第二个保持速度的值(以前是x = 1的值)并将其称为dx(delta X).当你得到输入时,你将速度增加一些小的dx = 0.01,以便在时间上速度逐渐增加.

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

因此,您具有基本的加速度,拖动和速度限制值

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

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

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;
}

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

设置CSS转换.

通过matrix命令设置CSS变换最简单.例如,设置默认变换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乘3 2D矩阵的缩短版本.

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

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

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)

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

因此,我根据角度,位置(x,y),比例(x,y)得到元素的值.然后我们按如下方式创建矩阵

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命中率低.

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设置相对于包含元素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).

演示:

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

每艘船的数学都在对象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用于发动机功率),模拟发动机提升功率和减速,这可以给人一种非常好的感觉.基本上它

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

什么适合你的游戏取决于你,你不必遵守规则,所以尝试不同的价值观和方法,直到你快乐.

标签:javascript,css,animation,css-transforms,game-physics
来源: https://codeday.me/bug/20191008/1873162.html