代码改变世界

【整理】HTML5游戏开发学习笔记(3)- 抛物线运动

  Benoly  阅读(606)  评论(0编辑  收藏  举报

1.预备知识
(1)Canvas旋转的实现过程

1
2
3
4
5
6
7
8
9
10
11
12
window.onload = function(){
     
    var ctx = document.getElementById('canvas1').getContext('2d')
 
    //旋转
    ctx.save()
    ctx.translate(200,200)//把(200,200)点作为临时的(0,0)点
    ctx.rotate(30*Math.PI/180)//顺时针旋转30度所对应的弧度
    ctx.fillRect(0,0,100,150)
    ctx.restore()
 
}


(2)抛物线运动中的重力加速度的模拟实现模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/*
    抛物线运动
    这个版本做了修改,为了减少一次角度和弧度之间的转换计算(通过Math.atan2可以直接算出弧度),
    构造参数中的角度改成了弧度
    deleted:
    angle = angle,                              //初始角度
    radians = angle*Math.PI/180,    //初始弧度
 
    object ParabolicMotion
        @velocity:初始速度
        @radians:初始弧度
        @gravity:初始加速度{x:0,y:2}
*/
function ParabolicMotion(velocity,radians,gravity){
    var velocity = velocity,
            radians = radians,
            gravity = gravity   
 
    var offsetX = velocity*Math.cos(radians),
            offsetY = -velocity*Math.sin(radians)
 
    function next(offset,gravity){
        var offset1 = offset
        var offset2 = offset+gravity
 
        return {offset:offset2,dv:(offset1+offset2)*.5}
    }
 
    return {
 
        /*
            获取运动轨迹到下一个时间点,x,y轴所偏移的距离
        */
        moveNext : function(){
            var offsetXD = next(offsetX,gravity.x)
            var offsetYD = next(offsetY,gravity.y)
             
            offsetX = offsetXD.offset
            offsetY = offsetYD.offset
 
            //console.log(offsetX+','+offsetY)
             
            return {x:offsetXD.dv,y:offsetYD.dv}
        }
 
    }
}

 


2.实现思路
涉及的对象,包括球(Ball),弹弓(Slingshot),抛物线运动(ParabolicMotion)。
操作过程是,鼠标键按下拖拽小球,和弹弓的作用点成一定的角度,鼠标键按起后,小球做抛物线运动

3.主要代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/*弹弓*/
function Slingshot(){
 
    var opts,
            ctx,
            crtBall,
            ballSelected = false
 
    function refresh(){
        ctx.clearRect(0,0,opts.width,opts.height)
 
        drawSling()
        crtBall.draw()                 
    }
 
    function drawSling(){
        var point = opts.actionPoint
 
        ctx.beginPath()
        ctx.moveTo(point.x,point.y)
        ctx.lineTo(point.x,opts.height)
        ctx.closePath()
        ctx.stroke()
 
        if(crtBall!=null&&ballSelected){
            //绘制连接球和弹弓的"橡皮筋"
            ctx.beginPath()
            ctx.moveTo(point.x,point.y)
            ctx.lineTo(crtBall.x,crtBall.y)
            ctx.closePath()
            ctx.stroke()
        }
    }
 
    function isBallSelected(offsetX,offsetY){
        var point = opts.actionPoint
        var a = Math.abs(point.x-offsetX)
        var b = Math.abs(point.y-offsetY)
        //var c = Math.sqrt(a*a+b*b)
 
        return (a*a+b*b)<=crtBall.radius*crtBall.radius
    }
 
    // 添加拉弹弓事件
    function initEvents(){
            var canvas = opts.canvas
            /*
                addEventListener函数的第3个参数,
                false表示内层元素事件先触发,ture则表示外层的事件先触发
                 
                alert(e.offsetX+','+e.offsetY)
                必须使用offsetX,offsetX是相对于canvas画布的距离(但firefox不支持)
 
                事件参数e没有考虑浏览器兼容问题
            */
            canvas.addEventListener('mousedown',function(e){                                                       
                // 判断球是否被选中
                ballSelected = isBallSelected(e.offsetX,e.offsetY)
                 
                if(ballSelected){
                    crtBall.locate(e.offsetX,e.offsetY)
                    refresh()                              
                }
            },false)
 
            canvas.addEventListener('mousemove',function(e){
                if(ballSelected){
                    crtBall.locate(e.offsetX,e.offsetY)
                    refresh()
                }
            },false)
 
            canvas.addEventListener('mouseup',function(e){
                if(ballSelected){
                    ballSelected = false
 
                    crtBall.locate(e.offsetX,e.offsetY)
                    refresh()          
 
                    //发射炮弹
                    fire(function(x,y){
                        //TODO 判断是否打中目标
 
                    })                             
                }
            },false)
    }
 
    function fire(onCompleted){
        // 获取当前的炮弹发射速度和弧度
        var velocity = ($.square(opts.actionPoint.y-crtBall.y)+$.square(opts.actionPoint.x-crtBall.x))/100
        var radians = -Math.atan2(opts.actionPoint.y-crtBall.y,opts.actionPoint.x-crtBall.x)//30*Math.PI/180
        var gravity = {x:0,y:2}
 
        var parabolicMotion = new ParabolicMotion(velocity,radians,gravity)
        var completed = false
 
        var timer = setInterval(function(){
            // 在当前抛物线轨迹下,获取炮弹下一次单位时间内x,y轴需要偏移的单位长度
            var offset = parabolicMotion.moveNext()
 
            crtBall.move(offset.x,offset.y)
            refresh()
             
            // 检查是否超出画布边界
            completed = (crtBall.x>=opts.width||crtBall.y>=opts.height)
 
            if(completed){
                clearInterval(timer)
 
                if(typeof onCompleted=='function'){
                    onCompleted(crtBall.x,crtBall.y)
                }          
            }                  
        },100)                 
    }
 
    return {
 
        init : function(options){
            opts = $.extend(options,{
                canvas : null,//画布
                width : 1000,//画布长
                height : 300,//画布高
                actionPoint : {x:150,y:200}/*作用力点坐标*/
            })
 
            ctx = opts.canvas.getContext('2d')
            drawSling()
 
            // 添加拉弹弓事件
            initEvents()
 
            return this
        },
 
        loadBall : function(ball){
            var point = opts.actionPoint
 
            crtBall = ball.init({ctx:ctx,x:point.x,y:point.y})
                                        .draw()
 
            return this
        }
 
    }
}

 

1
2
3
4
5
6
7
8
9
10
// app         
window.onload = function(){
     
    var canvas = document.getElementById('canvas1')
    var ball = new Ball(10)
    var slingshot = new Slingshot().init({canvas:canvas})          
 
    // 装载一个炮弹
    slingshot.loadBall(ball)
}

 



4.优化和完善
(1)需实现球打中目标物体后,目标物体进行旋转
(2)可以实现同时有多个小球发射,就像游戏里发射子弹的效果

点击右上角即可分享
微信分享提示