[javascript] Box2D JS初探(一个控制小球的游戏例子)。学习用!_box2d js b2massdata-程序员宅基地

技术标签: canvas  box2d  JS物理引擎  前端  前端 HTML5  javascript  

 Box2D 开源物理引擎,第一次接触,主要用途应该是做游戏,不过也可以作些简单的页面特性,比如切割粉碎。拖动带弹性系数。

从写了个控制小球运动,可以方向键控制左右和弹跳,碰到障碍物就死掉的DEMO来试试。

演示地址: null


这里注意的是,市面上有box2d JS 和 box2d Web2种js版本,有些小伙伴用老的box2d JS,那就不是这么写的了非常麻烦,而且box2d js不更新了,不建议学习使用。


<html>
   <head>
      <title>Box2dWeb example</title>
   </head>
   <body οnlοad="init();">
      <img id="tulip" style="display: none" src="33.jpg" alt="The Tulip" />
      <canvas id="canvas" width="900" height="600"></canvas>
   </body>
   <script type="text/javascript" src="Box2dWeb-2.1.a.3.min.js"></script>
   <script type="text/javascript">
      var world;
      
      function init() {
         var   b2Vec2 = Box2D.Common.Math.b2Vec2
         	,	b2BodyDef = Box2D.Dynamics.b2BodyDef
         	,	b2Body = Box2D.Dynamics.b2Body
         	,	b2FixtureDef = Box2D.Dynamics.b2FixtureDef
         	,	b2Fixture = Box2D.Dynamics.b2Fixture
         	,	b2World = Box2D.Dynamics.b2World
         	,	b2MassData = Box2D.Collision.Shapes.b2MassData
         	,	b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape
         	,	b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
         	,	b2DebugDraw = Box2D.Dynamics.b2DebugDraw
		,	b2MouseJointDef = Box2D.Dynamics.Joints.b2MouseJointDef
		,	b2WeldJointDef = Box2D.Dynamics.Joints.b2WeldJointDef
            ;
         
         world = new b2World(
               new b2Vec2(0,10)    //gravity  重力的方向,力度 单位牛,10就是重力加速度G
            ,  true                //allow sleep
         );
         
         var fixDef = new b2FixtureDef; //刚体创建
         fixDef.density = 1.0;    //密度
         fixDef.friction = 0.5;   //摩擦力 加大了之后正方形被推动就比较不容易动
         fixDef.restitution = 0.8;  //弹性系数,1应该就是不衰减,大于1反而越来越快
         
         var bodyDef = new b2BodyDef;
         
         //create ground
         bodyDef.type = b2Body.b2_staticBody;
         bodyDef.position.x = 1;  //单位是米,一米是30像素。
         bodyDef.position.y = 19.8;
	 //以下试图建立四边框(用刚体来做,游戏四边的场景边框,不然会掉出去)
         fixDef.shape = new b2PolygonShape;
         fixDef.shape.SetAsBox(30, 0.1);   //长宽应该(宽长,xy对应)
         world.CreateBody(bodyDef).CreateFixture(fixDef);
         fixDef.shape.SetAsBox(0.1, 20);
         bodyDef.position.Set(0.1, 0.1);
         world.CreateBody(bodyDef).CreateFixture(fixDef);
	 fixDef.shape.SetAsBox(30, 0.1);
         bodyDef.position.Set(0.1, 0.1);
         world.CreateBody(bodyDef).CreateFixture(fixDef);
	 fixDef.shape.SetAsBox(0.1, 20);
         bodyDef.position.Set(29.8, 0.1);
         world.CreateBody(bodyDef).CreateFixture(fixDef);
	 //在场景中制作台阶
	 fixDef.shape.SetAsBox(3, 0.1);
	 bodyDef.position.Set(10, 18.5);
         world.CreateBody(bodyDef).CreateFixture(fixDef);
	 
	 var mouseX, mouseY,selectBody, keyBoll,vX, vY,keyVelocity,Death = false,barrier;
         var canvasPosition = getElementPosition(document.getElementById("canvas"));
	 var context = document.getElementById("canvas").getContext("2d");
	 var img=document.getElementById("tulip");
	 //在场景中的空中放置一个正方形
	 bodyDef.type = b2Body.b2_dynamicBody;
	 fixDef.shape = new b2PolygonShape();
         fixDef.shape.SetAsBox(1,1); //30px * 30px 的矩形
	 fixDef.restitution = 2;  //改变弹性系数,如果撞到他会更快的反弹
	 bodyDef.position.Set(10,13); //矩形陷阱的放置位置
	 console.log(fixDef.shape.GetVertices() );
	 console.log(typeof fixDef.shape.GetVertices()[0] );
         selectBody = world.CreateBody(bodyDef).CreateFixture(fixDef).GetBody();
	 //建个链接器。把矩形固定在场景中
	 var jointDef = new b2MouseJointDef();
	 jointDef.bodyA = world.GetGroundBody(); 
         jointDef.bodyB = selectBody;
	 //矩形和场景本体相连
         jointDef.target.Set(10, 13);
	 //连接位置
         jointDef.collideConnected = true;
	 //是否碰撞。要碰。不然就是根线,不能固定用
         jointDef.maxForce = 3000.0;
	 //最大力,如果设置过小,会被从固定位置撞开
	 world.CreateJoint(jointDef);
	 //连接器加入世界
	 // 创建钉子
	 fixDef.restitution = 0;
	 fixDef.friction = 5000
	 fixDef.shape = new b2PolygonShape;
	 var vArray = [new b2Vec2(0,-0.5),new b2Vec2(0.25,0.5),new b2Vec2(-0.25,0.5)]
	 fixDef.shape.SetAsVector(vArray, 3);
	 //创建一个三角形这里要注意了,Vector字面是向量,其实穿进去的是三角形顶点坐标List,3是顶点个数。而且要顺时针方向,不然无法物理判定。
	 bodyDef.position.Set(15,19.5);
	 barrier = world.CreateBody(bodyDef).CreateFixture(fixDef).GetBody();
	 //创建连接,这个WeldJoint看字面就知道,是刚体连接,上面那个连接会被力改变,会旋转。这个就是焊死了。相当于固定
	 jointDef = new b2WeldJointDef();
	 jointDef.bodyA = world.GetGroundBody();
	 jointDef.bodyB = barrier;
	 jointDef.localAnchorA = new b2Vec2(15, 19.3)
	 jointDef.localAnchorB = barrier.GetLocalCenter();
         jointDef.collideConnected = true;
         
	 world.CreateJoint(jointDef);
	 //创建主角--一个球 
	 fixDef.restitution = 0.8;
	 fixDef.shape = new b2CircleShape(1);
	 bodyDef.position.Set(1,19.5);
	 bodyDef.userData=img;
	 //给球上图片
         keyBoll = world.CreateBody(bodyDef).CreateFixture(fixDef).GetBody();
	 //把这个球的body记录下来。
	 /**
	 这里是点击小球在鼠标位置降落下来的 监听。
	 document.addEventListener("mousedown", function(e) {
	    mouseX = (e.layerX - canvasPosition.x) / 30;
            mouseY = (e.layerY - canvasPosition.y) / 30; 
         }, true);
	 
	 
	 document.addEventListener("mouseup", function(e) {
	    fixDef.shape = new b2CircleShape(0.5);
	    bodyDef.position.Set(mouseX,mouseY);
            selectBody = world.CreateBody(bodyDef).CreateFixture(fixDef).GetBody();
	    vX = (e.layerX - canvasPosition.x) / 30 - mouseX;
	    vY = (e.layerY - canvasPosition.y) / 30 - mouseY;
	    selectBody.ApplyImpulse( new b2Vec2(vX*5,vY*5), selectBody.GetLocalCenter());
         }, true);
	 **/
	 
	 //监控键盘事件
	 document.addEventListener("keydown", function(e) {
	    var theEvent = window.event || e;
	    var code = theEvent.keyCode || theEvent.which;
	    console.log(code);
	    var keyVelocity = keyBoll.GetLinearVelocity();
	    //按上方向键:
	    if(code==38){
	       //判定是不是y方向速度为0 ,我要球落地挺稳才让他继续下一次起跳,不然会越来越高。
	      if (keyVelocity.y == 0) {
	       //给一个y方向20的向上加速度。算下10的重力。应该也就2秒就下落了。
	       keyBoll.ApplyImpulse(new b2Vec2(0,-20), keyBoll.GetWorldCenter());
	      }
	    }
	    //左按键
	    if(code==37){
	      keyBoll.SetAwake(true);
	      //唤醒目标,并直接给向左10的速度,这里和上不一样,是直接替换速度,并不是加速度,所以Y方向的速度还需要加入。
	      keyBoll.SetLinearVelocity(new b2Vec2(-10,keyVelocity.y), keyBoll.GetWorldCenter());
	    }
	    //右按键
	    if(code==39){
	      keyBoll.SetAwake(true);
	      //同理
	      keyBoll.SetLinearVelocity(new b2Vec2(10,keyVelocity.y), keyBoll.GetWorldCenter());
	    }
 
         }, false);
	 //监听按键弹起,并给个减速(也可以直接熟读至0)看个人喜好。
	 document.addEventListener("keyup", function(e) {
	    var theEvent = window.event || e;
	    var code = theEvent.keyCode || theEvent.which;
	    console.log(code);
	    if(code==37){
	      keyBoll.ApplyImpulse(new b2Vec2(10,0), keyBoll.GetWorldCenter());
	    }
	    if(code==39){
	      keyBoll.ApplyImpulse(new b2Vec2(-10,0), keyBoll.GetWorldCenter());
	    }
	   
         }, false);
	 
         //setup debug draw
         var debugDraw = new b2DebugDraw();
			debugDraw.SetSprite(document.getElementById("canvas").getContext("2d"));
			debugDraw.SetDrawScale(30.0);
			debugDraw.SetFillAlpha(0.3);
			debugDraw.SetLineThickness(2.0);
			debugDraw.SetFlags(b2DebugDraw.e_shapeBit | b2DebugDraw.e_jointBit);
			world.SetDebugDraw(debugDraw);
         
         window.setInterval(update, 1000 / 60); //配置侦数
	 
	 
	 
	 //官网的例子里的一个canvans定位函数
         function getElementPosition(element) {
            var elem=element, tagname="", x=0, y=0;
           
            while((typeof(elem) == "object") && (typeof(elem.tagName) != "undefined")) {
               y += elem.offsetTop;
               x += elem.offsetLeft;
               tagname = elem.tagName.toUpperCase();

               if(tagname == "BODY")
                  elem=0;

               if(typeof(elem) == "object") {
                  if(typeof(elem.offsetParent) == "object")
                     elem = elem.offsetParent;
               }
            }

            return {x: x, y: y};
         }
	 
	 function update() {
	   
	 var a = 1 / 60;
	 //如果2个球被被唤醒了,并且球和钉子在一定范围内,则说明撞到钉子了。GameOver 并且把刷新率置99999,表示不动了。
	 if (barrier.IsAwake() && keyBoll.IsAwake()) {
	    if (barrier.GetPosition().x + 2 > keyBoll.GetPosition().x && barrier.GetPosition().x - 2 < keyBoll.GetPosition().x ) {
	       alert("Game Over");
	       a = 999999;
	    }
	    console.log(barrier.GetPosition());
	    console.log(keyBoll.GetPosition()); 
	 }
         world.Step(
               a   //frame-rate
            ,  10      //velocity iterations 
            ,  10      //position iterations
         );
         world.DrawDebugData();
	 //贴图
	 context.save();
	 context.translate(keyBoll.GetPosition().x*30,keyBoll.GetPosition().y*30);
	 context.rotate(keyBoll.GetAngle());
	 context.drawImage(keyBoll.GetUserData(),-keyBoll.GetUserData().width/2,-keyBoll.GetUserData().height/2);
	 context.restore(); 
         world.ClearForces();
      };
      };
      
      
   
   </script>
   
   
</html>

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/wty19/article/details/24022571

智能推荐

鲲鹏gcc安装与使用-程序员宅基地

文章浏览阅读1.4k次。鲲鹏GCC安装与使用1. 获取安装包略2. 设置安装目录创建鲲鹏GCC安装目录(这里以/opt/aarch64/compiler为例)mkdir -p /opt/aarch64/compil将鲲鹏GCC压缩包拷贝到安装目录cp -rf gcc-9.3.1-2021.03-aarch64-linux.tar.gz /opt/aarch64/compil3. 进入压缩包目录,执行命令解压缩软件包。tar -xf gcc-9.3.1-2021.03-aarch64-linux.ta

SyntaxError:(unicode error)‘unicodeescape’-程序员宅基地

文章浏览阅读5.9k次,点赞6次,收藏13次。python出现下面的错误:原因是路径写错。将路径修改为:_unicodeescape

Linux串口原理与编程-程序员宅基地

文章浏览阅读468次。Linux C++ 串口通信随着嵌入式开发在物联网行业中站的比重日益增加,Linux 环境下的C++也不断变得更为大众化。习惯了Window平台开发的开发人员, 都被Visual Studio的强大宠坏了, 无论是什么样的开发需求, 总能有现成的轮子可以直接拿来用。就好比这里要介绍的串口通信, 在Windows开发中, 无论是C++, MFC,还是C#, 巨硬大大都给我们做好了封装。可是在Linux下就没那么简单了,虽然开源, 但是很多的开发都偏底层,连一个标准库级别的串口通信SDK都没有,很是无奈。._linux串口原理

接口调用get,简易版_"export const projectdetail = param => { return re-程序员宅基地

文章浏览阅读244次。apiapi>index.jsimport request from '@/utils/request'export const 接口名= (data) => { return request('后台接口路径','get')}文件名.vue<template> <div class="container"> <p>{{this.onething}}</p>//接口调用后引用的地方 </div></_"export const projectdetail = param => { return request.get(\"/ctf/infocollect"

小工具_小工具 percentage-程序员宅基地

文章浏览阅读148次。请求网络资源public class ReadJsonUtils {public static String read(InputStream inputStream,String str){ try { byte[] b = new byte[1024]; int len; ByteArrayOutputStream arrayOu_小工具 percentage

Java常见面试题-100道_java面试题100题-程序员宅基地

文章浏览阅读3.3k次。面试题1、 overload 和override的区别 ?overload的方法是否可以改变返回值的类型?Overload:重载 Override:重写重载:同一个类中,方法名相同,参数列表不同。与返回值类型无关。重写:存在于子父类中,或者子父接口中,方法名相同,参数列表相同。遵循”两同两小一大“规则:两同:方法名相同,参数类型相同两小:类型:基本类型:子类返回类型必须等于父类方法返回的类型引用类型:子类返回类型小于等于父类方法返回类型,异常:子类抛..._java面试题100题

随便推点

极光推送使用教程_极光上架教程-程序员宅基地

文章浏览阅读599次。ndk { //选择要添加的对应cpu类型的.so库。 abiFilters 'armeabi', 'armeabi-v7a', 'armeabi-v8a' // 还可以添加 'x86', 'x86_64', 'mips', 'mips64' } manifestPlaceholder_极光上架教程

less超详细介绍_.less-程序员宅基地

文章浏览阅读6.1k次,点赞6次,收藏31次。一、Less介绍1、官方介绍Less 是一门 CSS 预处理语言,它扩充了 CSS 语言,增加了诸如变量、混合(mixin)、函数等功能,让 CSS 更易维护、方便制作主题、扩充。Less 可以运行在 Node 或浏览器端。2、自己理解Less是一门动态CSS语言,使得CSS样式更加灵活地作用于Html标签。试想如果没有Less,我们要对样式做一些逻辑计算的时候只能依靠Js去实现,有了Less之后,可以方便地动态给Html标签设置样式。比如一个最常见的需求,在当前浏览器的宽度小于500px的_.less

jq load无效_jQuery应用load()方法后js失效的问题分析与解决-程序员宅基地

文章浏览阅读1.6k次。load()方法算是最简单的ajax了,非常方便,不过也有一些难以理解的地方。比如不久前我出现了这样一个问题:我的代码高亮是用pretty,它的特点是不破坏源代码,用js动态使代码着色,在载入正文后使用prettyPrint()函数使代码高亮生效。我在另一个页面使用jQuery中的load()方法载入此页面的正文,但是代码区的高亮消失了,也就是说js代码未运行。这是可以理解的,因为load()方法..._jquery datagrid load无效

原创 | 程序员学英语,如何做才能飞起!-程序员宅基地

文章浏览阅读179次。老铁们,学习一门语言,意味着很多,既能增加一种思考维度,也能训练我们的意志力,同时它也代表着一种生活态度。我在今年6月总结(六月总结和思考)中,决心利用工作之外的时间学习..._aware of the economic benefits of railroads 成分

当当电子书生成pdf示例_当当电子书怎么下载成pdf-程序员宅基地

文章浏览阅读4.4k次。准备工作安装Python 3、PyCharm 社区版安装 pyautogui 命令如下:(参考:pyautogui安装教程)pip install pyautogui或者pip install pyautogui -i https://mirrors.aliyun.com/pypi/simplepyautogui使用参考:https://blog.csdn.net/ibiao/article/details/77859997https://jingyan.baidu.com.._当当电子书怎么下载成pdf

【大数据】Apache Iceberg 概述和源代码的构建-程序员宅基地

文章浏览阅读1.2k次,点赞5次,收藏6次。我们在使用不同的引擎进行大数据计算时,需要将数据根据计算引擎进行适配。这是一个相当棘手的问题,为此出现了一种新的解决方案:介于上层计算引擎和底层存储格式之间的一个中间层。这个中间层不是数据存储的方式,只是定义了数据的元数据组织方式,并向计算引擎提供统一的类似传统数据库中 “表” 的语义。它的底层仍然是 Parquet、ORC 等存储格式。基于此,Netflix 开发了 Iceberg,目前已经是 Apache 的顶级项目。_apache iceberg

推荐文章

热门文章

相关标签