如何在 html5 canvas 中正确实现重力?

问题描述

我是画布和游戏引擎的新手。我在以下代码段中有一些随机弹跳球。它似乎工作正常,直到这些球堆积起来,如果我在检查碰撞时不减去重力,它们最终会坍塌并重叠。然而,减去重力会使那些球疯狂地跳舞/摇晃,我相信这不是模拟重力和碰撞的正确方法

我知道有一些很棒的 JS 游戏引擎,但我想了解如何在游戏中正确实现重力。

P.S.:点击画布以生成球。

let GAMEOBJ=[{R:50,N:1}];
let RADIUS=30;
let GraviTY=9.81;


let _UPDATE=function(C){
    return{
        INERTIA:B=>{
            this.CO=false;
            this.DPR=C.DPR;
            if(!this.R)this.R=Math.max(15,Math.random()*RADIUS);
            this.RR=this.R*this.DPR;
            if(!this.V)this.V={};       //V=veLocity
            if(!this.V.R)this.V.R=Math.min(.99,1/this.R*(GraviTY));
            ['X','Y'].map(D=>{
                if(undefined==this[D])this[D]=Math.random()*C[D];
                if(undefined==this.V[D])this.V[D]=C.RSP();
                if(B){
                    if(C[D]-this.RR<=this[D]||this.RR>=this[D]){
                        this.V[D]=this.V[D]*-1;
                        this.V[D]-=this.V[D]*(1-this.V.R);
                        if(this.RR>=this[D])this[D]=this.RR
                        else// if(C[D]-this.RR<=this[D])
                        this[D]=C[D]-this.RR;
                    }
                }else this[D]=C[D]<=this[D]?0:0>=this[D]?C[D]:this[D];
            });
            this.V.X-=this.V.X*(1-this.V.R)/100;
            this.V.Y+=GraviTY*this.DPR;
        },POSITION:()=>{
            ['X','Y'].map(D=>{
                this[D]+=this.V[D]*C.LOOP.DELTA;
            });
            this.V.A=Math.atan2(this.V.Y,this.V.X);     //A=angle
        }
    };
};
let _COLLISION=function(){
    return{
        CIRCLE:O=>{
            if(!this.V)this.V={};
            if(!this.V.D)this.V.D={};   //D=distance
            ['X','Y'].map(D=>{
                this.V.D[D]=O[D]-this[D];
            });
            this.V.D.D=Math.sqrt(this.V.D.X*this.V.D.X+this.V.D.Y*this.V.D.Y);
            if(this.V.D.D<this.RR+O.RR){
                this.CO=O;  //CO=collision object
                O.CO=this;
                this.COLLISION().PHYSIC();
            }
        },PHYSIC:()=>{
            this.V.CN={     //CN=collision normalized
                X:this.V.D.X/this.V.D.D,Y:this.V.D.Y/this.V.D.D
            };

            this.V.S=(this.V.X-this.CO.V.X)*this.V.CN.X+(this.V.Y-this.CO.V.Y)*this.V.CN.Y; //S=speed
            if(this.V.S>0){
                this.V.I=2*this.V.S/(this.V.R+this.CO.V.R);     //I=impulse
                ['X','Y'].map(D=>{
                    this.V[D]-=this.V.CN[D]*this.V.I*this.V.R;
                    this.CO.V[D]+=this.V.CN[D]*this.V.I*this.CO.V.R;
                });
                this.V.Y-=GraviTY*this.DPR;
            }
        }
    };
};

CanvasRenderingContext2D.prototype.PUSH=function(O){
    if(!this.OBJ)this.OBJ=[];
    if(!O||'object'!=typeof O)O={};
    O.UPDATE=_UPDATE;
    O.COLLISION=_COLLISION;
    this.OBJ.push(O);
};

CanvasRenderingContext2D.prototype.DRAW=function(I){
    return{
        CIRCLE:()=>{
            this.translate(this.OBJ[I].X,this.OBJ[I].Y);
            this.rotate(this.OBJ[I].V.A);
            this.translate(-this.OBJ[I].X,-this.OBJ[I].Y);

            this.fillStyle=this.OBJ[I].S?'red':this.OBJ[I].CO?'#ff8080':'#0099b0';//this.OBJ[I].C;
            this.beginPath();
            this.arc(this.OBJ[I].X,this.OBJ[I].Y,this.OBJ[I].RR,2*Math.PI);
            this.fill();
            this.closePath();
                
            this.strokeStyle='white';
            this.linewidth=1*this.DPR;
            this.beginPath();
            this.moveto(this.OBJ[I].X,this.OBJ[I].Y);
            this.lineto(this.OBJ[I].X+(this.OBJ[I].RR),this.OBJ[I].Y);
            this.stroke();
            this.closePath();

            this.setTransform(1,1,0); //reset transform
        },TEXT:STR=>{
            this.fillStyle='red';
            this.font=`${24*this.DPR}px Arial`;
            this.textAlign='left';
            this.textBaseline='top';
            this.fillText(STR,10,10);
        }
    }
};

HTMLCanvasElement.prototype.RSP=function(){     //RSP=random speed
    return(Math.random()*this.width*2-this.width);
};
HTMLCanvasElement.prototype.RESIZE=function(){
    this.DPR=devicePixelRatio;
    this.width=this.X=window.innerWidth*this.DPR;//screen.width;
    this.height=this.Y=window.innerHeight*this.DPR;//screen.height;
}
HTMLCanvasElement.prototype.INIT=function(CTNR,L){      //CTNR=container
    (window.onresize=window.onorientationchange=()=>this.RESIZE())();

    let CTX=this.getContext('2d');
    if(!CTNR)CTNR=[];
    if(L)CTNR=CTNR.concat(Array.apply(0,Array(L)));
    CTNR.map(O=>CTX.PUSH(O));
    
    this.LOOP={
        DRAW:()=>{
            requestAnimationFrame(T=>{
                //this.RESIZE();
                CTX.DPR=this.DPR;

                CTX.OBJ.map(O=>O.UPDATE(this).INERTIA(true));

                //*/
                CTX.OBJ.map(O1=>CTX.OBJ.map(O2=>{
                    if(O1!=O2)O1.COLLISION().CIRCLE(O2);
                }));
                //*/

                CTX.OBJ.map(O=>O.UPDATE(this).POSITION());

                //this.width=this.width;
                CTX.clearRect(0,this.width,this.height);  //clear the canvas

                CTX.OBJ.map((O,I)=>CTX.DRAW(I).CIRCLE());

                CTX.DRAW().TEXT(`FPS: ${this.LOOP.FPS(T)}`);

                this.LOOP.DRAW();
            });
        },DELTA:1,FPS:(T,LT)=>{
            LT=this.LOOP.LT||0;
            this.LOOP.DELTA=(T-LT)/1000;
            this.LOOP.LT=T;
            return(Math.round(1/this.LOOP.DELTA));
        }
    };
    this.LOOP.DRAW();

    this.onclick=function(){
        /*
        CTX.OBJ.map(O=>{
            O.Y--;
            O.V.Y=(GraviTY*O.DPR*O.V.R*50)*-1;
        });
        //*/
        this.getContext('2d').PUSH({Y:0,V:{Y:0}});
    };
};

window.onload=()=>CANVAS.INIT(GAMEOBJ,10);
/*tmP*/
*{Box-sizing:border-Box;}
*:focus{outline:none;}
html,body{
    overflow:hidden;
    user-select:none;
    -webkit-user-select:none;
    -webkit-touch-callout:none;
    -webkit-text-size-adjust:100%;
    -webkit-tap-highlight-color:transparent;
    margin:0;
    padding:0;
    height:100%;
    /*font-family:'Noto Sans HK',sans-serif;*/
    color:#777;
    position:fixed;
    left:0;
    right:0;
    top:0;
    bottom:0;
}
img{
    max-width:100%;
    user-drag:none;
    -webkit-user-drag:none;
}
main{
    display:block;
    position:fixed;
    z-index:2;
    left:0;
    right:0;
    top:0;
    bottom:0;
    width:100%;
    height:100%;
    overflow:hidden;
    /*
    overflow-x:hidden;
    overflow-y:scroll;*/
    -webkit-overflow-scrolling:touch;
}

[CC]{border:1px dotted red;}
[W100]{width:100%;}
[H100]{height:100%;}
<!DOCTYPE HTML>
<html prefix='og: http://ogp.me/ns#' lang='zh'>
<Meta charset='UTF-8'>
<Meta name='viewport' content='width=device-width,initial-scale=1'>
<link rel='icon' href='asset/img/favicon.ico'>

<link rel='stylesheet' href='asset/style/game.css'>

<body>
    <code hidden>prototype.cloud GAME</code>
    <main>
        <canvas id='CANVAS' W100 CC>
            <code>Your browser does not support the HTML5 canvas tag</code>
        </canvas>
    </main>
</body>
<script defer type='module' src='asset/script/game.js'></script>

解决方法

暂无找到可以解决该程序问题的有效方法,小编努力寻找整理中!

如果你已经找到好的解决方法,欢迎将解决方案带上本链接一起发送给小编。

小编邮箱:dio#foxmail.com (将#修改为@)