使用 Box2D 做一个 JansenWalker 机器人

在 Box2DFlash
的官网
的首页有一个小 Demo,这个 Demo
中有11个例子,可以通过左右方向键查看不同的例子,里面的每个例子都非常有趣,但最让我感兴趣的,是其中一个叫 JansenWalker
的,里面是一个往右移动的机器人,有6只脚,交替着地往右边行走,如下图:

前段时间在看 Box2D,把官网下载下来的 Demo
源码都看完并写一遍,其他的例子花的时间都不多,这个花的时间有点长,主要是分析结构,然后就是各个关节的比例,身体的大小,不断的微调,找到合适的数值,以下是我最终完成的效果:

我加了4条腿,当然,理论上是可以N条腿的,腿越多越稳定,理论上是这样,不过实际结果如何有兴趣的童鞋可以自己试试,接下来我把我的制作过程记录下来。

预备知识

完成这个 Demo 需要用到一些 Box2D 的特性,以下,是你需要知道的东西:

1、创建圆形刚体
2、创建矩形刚体
3、创建多边形刚体
4、创建距离关节
5、创建旋转关节
6、二维矩阵变换

因为每次创建刚体都要写很多代码,所以我包装了一部分创建刚体和关节的代码,所有的代码都实现在 b2Utils 这个类里面,以下是代码:

package org.easily.box2d
{
import Box2D.Collision.Shapes.b2CircleShape;
import Box2D.Collision.Shapes.b2PolygonShape;
import Box2D.Common.Math.b2Vec2;
import Box2D.Dynamics.Contacts.b2Contact;
import Box2D.Dynamics.Contacts.b2ContactEdge;
import Box2D.Dynamics.Joints.b2DistanceJoint;
import Box2D.Dynamics.Joints.b2DistanceJointDef;
import Box2D.Dynamics.Joints.b2MouseJoint;
import Box2D.Dynamics.Joints.b2MouseJointDef;
import Box2D.Dynamics.Joints.b2RevoluteJoint;
import Box2D.Dynamics.Joints.b2RevoluteJointDef;
import Box2D.Dynamics.b2Body;
import Box2D.Dynamics.b2BodyDef;
import Box2D.Dynamics.b2DebugDraw;
import Box2D.Dynamics.b2Fixture;
import Box2D.Dynamics.b2FixtureDef;
import Box2D.Dynamics.b2World;

import flash.display.Sprite;
import flash.display.Stage;
import flash.events.Event;
import flash.events.MouseEvent;

/**
* box2d utils
* @author Easily
*/
public class b2Utils
{
public static const frameRate:Number = 30;
public static const timeStep:Number = 1.0 / frameRate;
public static const velIterations:int = 10;
public static const posIterations:int = 10;
public static const worldScale:Number = 30;

private static function convertVec2(vec2:b2Vec2):b2Vec2
{
vec2 = vec2.Copy();
vec2.x /= worldScale;
vec2.y /= worldScale;
return vec2;
}

public static function createRect(world:b2World, type:uint, pos:b2Vec2, size:b2Vec2, density:Number, friction:Number, restitution:Number, filterIndex:int = 0):b2Body
{
pos = convertVec2(pos);
size = convertVec2(size);

var bodyDef:b2BodyDef = new b2BodyDef();
bodyDef.position.Set(pos.x, pos.y);
bodyDef.type = type;

var shapeDef:b2PolygonShape = new b2PolygonShape();
shapeDef.SetAsBox(size.x, size.y);

var fixtureDef:b2FixtureDef = new b2FixtureDef();
fixtureDef.density = density;
fixtureDef.friction = friction;
fixtureDef.restitution = restitution;
fixtureDef.shape = shapeDef;
fixtureDef.filter.groupIndex = filterIndex;

var body:b2Body = world.CreateBody(bodyDef);
body.CreateFixture(fixtureDef);
return body;
}

public static function createRect2(world:b2World, type:uint, pos:b2Vec2, size:b2Vec2, offset:b2Vec2, angle:Number, density:Number, friction:Number, restitution:Number, filterIndex:int = 0):b2Body
{
pos = convertVec2(pos);
size = convertVec2(size);
offset = convertVec2(offset);

var bodyDef:b2BodyDef = new b2BodyDef();
bodyDef.position.Set(pos.x, pos.y);
bodyDef.type = type;

var shapeDef:b2PolygonShape = new b2PolygonShape();
shapeDef.SetAsOrientedBox(size.x, size.y, offset, Math.PI / 180 * angle);

var fixtureDef:b2FixtureDef = new b2FixtureDef();
fixtureDef.density = density;
fixtureDef.friction = friction;
fixtureDef.restitution = restitution;
fixtureDef.shape = shapeDef;
fixtureDef.filter.groupIndex = filterIndex;

var body:b2Body = world.CreateBody(bodyDef);
body.CreateFixture(fixtureDef);
return body;
}

public static function createPolygon(world:b2World, type:uint, pos:b2Vec2, vertices:Object, density:Number, friction:Number, restitution:Number, filterIndex:int = 0):b2Body
{
pos = convertVec2(pos);
var vertices_:Array = [];
for each (var vertex:b2Vec2 in vertices)
{
vertices_.push(convertVec2(vertex));
}

var bodyDef:b2BodyDef = new b2BodyDef();
bodyDef.position.Set(pos.x, pos.y);
bodyDef.type = type;

var shapeDef:b2PolygonShape = new b2PolygonShape();
shapeDef.SetAsArray(vertices_, vertices_.length);

var fixtureDef:b2FixtureDef = new b2FixtureDef();
fixtureDef.density = density;
fixtureDef.friction = friction;
fixtureDef.restitution = restitution;
fixtureDef.shape = shapeDef;
fixtureDef.filter.groupIndex = filterIndex;

var body:b2Body = world.CreateBody(bodyDef);
body.CreateFixture(fixtureDef);
return body;
}

public static function createCircle(world:b2World, type:uint, pos:b2Vec2, radius:Number, density:Number, friction:Number, restitution:Number, filterIndex:int = 0):b2Body
{
pos = convertVec2(pos);
radius /= worldScale;

var bodyDef:b2BodyDef = new b2BodyDef();
bodyDef.position.Set(pos.x, pos.y);
bodyDef.type = type;

var shapeDef:b2CircleShape = new b2CircleShape(radius);

var fixtureDef:b2FixtureDef = new b2FixtureDef();
fixtureDef.density = density;
fixtureDef.friction = friction;
fixtureDef.restitution = restitution;
fixtureDef.shape = shapeDef;
fixtureDef.filter.groupIndex = filterIndex;

var body:b2Body = world.CreateBody(bodyDef);
body.CreateFixture(fixtureDef);
return body;
}

public static function createWall(world:b2World, w:Number, h:Number, thickness:Number, density:Number, friction:Number, restitution:Number):void
{
var up:b2Body = createRect(world, b2Body.b2_staticBody, new b2Vec2(w / 2, 0), new b2Vec2(w / 2, thickness), density, friction, restitution);
var bottom:b2Body = createRect(world, b2Body.b2_staticBody, new b2Vec2(w / 2, h), new b2Vec2(w / 2, thickness), density, friction, restitution);
var left:b2Body = createRect(world, b2Body.b2_staticBody, new b2Vec2(0, h / 2), new b2Vec2(thickness, h / 2), density, friction, restitution);
var right:b2Body = createRect(world, b2Body.b2_staticBody, new b2Vec2(w, h / 2), new b2Vec2(thickness, h / 2), density, friction, restitution);
}

public static function createDebug(host:Sprite, world:b2World, flags:uint, alpha:Number):void
{
var sprite:Sprite = new Sprite();
host.addChild(sprite);

var debug:b2DebugDraw = new b2DebugDraw();
debug.SetSprite(sprite);
debug.SetDrawScale(worldScale);
debug.SetFlags(flags);
debug.SetFillAlpha(alpha);
world.SetDebugDraw(debug);
}

public static function mouseToWorld(stage:Stage):b2Vec2
{
return new b2Vec2(stage.mouseX / worldScale, stage.mouseY / worldScale);
}

public static function mouseJoint(stage:Stage, world:b2World):void
{
var mouseJoint:b2MouseJoint;
stage.addEventListener(MouseEvent.MOUSE_DOWN, createJoint);
function createJoint(event:MouseEvent):void
{
world.QueryPoint(queryPoint, mouseToWorld(stage));
}
function queryPoint(fixture:b2Fixture):Boolean
{
var body:b2Body = fixture.GetBody();
if (body.GetType() == b2Body.b2_dynamicBody)
{
var jointDef:b2MouseJointDef = new b2MouseJointDef();
jointDef.bodyA = world.GetGroundBody();
jointDef.bodyB = body;
jointDef.target = mouseToWorld(stage);
jointDef.maxForce = 1000 * body.GetMass();
mouseJoint = world.CreateJoint(jointDef) as b2MouseJoint;
stage.addEventListener(MouseEvent.MOUSE_MOVE, moveJoint);
stage.addEventListener(MouseEvent.MOUSE_UP, killJoint);
}
return false;
}
function moveJoint(event:MouseEvent):void
{
mouseJoint.SetTarget(mouseToWorld(stage));
}
function killJoint(event:MouseEvent):void
{
world.DestroyJoint(mouseJoint);
stage.removeEventListener(MouseEvent.MOUSE_MOVE, moveJoint);
stage.removeEventListener(MouseEvent.MOUSE_UP, killJoint);
}
}

public static function distanceJoint(world:b2World, bodyA:b2Body, bodyB:b2Body, localAnchorA:b2Vec2, localAnchorB:b2Vec2, length:Number):b2DistanceJoint
{
localAnchorA = convertVec2(localAnchorA);
localAnchorB = convertVec2(localAnchorB);
length /= worldScale;

var jointDef:b2DistanceJointDef = new b2DistanceJointDef();
jointDef.bodyA = bodyA;
jointDef.bodyB = bodyB;
jointDef.localAnchorA = localAnchorA;
jointDef.localAnchorB = localAnchorB;
jointDef.length = length;
var joint:b2DistanceJoint = world.CreateJoint(jointDef) as b2DistanceJoint;
return joint;
}

public static function revoluteJoint(world:b2World, bodyA:b2Body, bodyB:b2Body, localAnchorA:b2Vec2, localAnchorB:b2Vec2, enableMotor:Boolean = false, motorSpeed:Number = 0, maxMotorTorque:Number = 0):b2RevoluteJoint
{
localAnchorA = convertVec2(localAnchorA);
localAnchorB = convertVec2(localAnchorB);

var jointDef:b2RevoluteJointDef = new b2RevoluteJointDef();
jointDef.bodyA = bodyA;
jointDef.bodyB = bodyB;
jointDef.localAnchorA = localAnchorA;
jointDef.localAnchorB = localAnchorB;

if (enableMotor)
{
jointDef.enableMotor = true;
jointDef.motorSpeed = motorSpeed;
jointDef.maxMotorTorque = maxMotorTorque;
}

var joint:b2RevoluteJoint = world.CreateJoint(jointDef) as b2RevoluteJoint;
return joint;
}
}
}

b2Utils

简单介绍一下里面的方法:

- createRect 创建矩形,没有旋转
- createRect2 同上,但可以指定旋转角度以及相对中心点的偏移
-
createPolygon 创建多边形,传入一个点数组
- createCircle
创建圆形
- createWall
创建包围盒,方便测试
- createDebug
创建调试绘图
- mouseJoint
鼠标关节
- distanceJoint
距离关节
- revoluteJoint
旋转关节

其中需要注意的是 createPolygon,参数中的点数组,点的顺序必须是顺时针,逆时针的时候碰撞检测失效。

结构分析

一开始我盯着这个机器人盯了很久,因为每条腿的颜色都是一样的,所以看起来眼睛有点花,不过最后还是搞明白了基本的结构。这个机器人由几种基本的形状组成的,其中的三角形具有稳定性,因而不发生形变,而两个四边形在中间的节点的带动下发生形变,从而带动腿的运动。我自己在纸上画了一些草图,不过都非常凌乱。后面我
Google 了一下,发现原来这玩意最开始是一个实体的机器人,利用风能驱动,找到一张 gif 可以比较直观的看到它的结构:

从图中可以看到,主要结构由身体加两边的腿组成,身体是由一个圆形和矩形物体组合成,圆形旋转作为整个机器人的驱动源,腿由两个三角形组成,一个机器人可能会有N条腿。

创建身体

身体由一个圆和矩形组成,然后在圆和矩形的中心点创建一个旋转关节,并且马达开关打开,马达速度为整形,正负分别代表顺时针和逆时针转动,代码如下:


var type:int = b2Body.b2_dynamicBody;
var density:Number = 2;
var friction:Number = 0.5;
var resititution:Number = 0.3;
var radius:Number = 28;
var size:b2Vec2 = new b2Vec2(45, radius / 2);

var rect:b2Body = b2Utils.createRect(world, type, new b2Vec2, size, density, friction, resititution, bodyIndex);
var circle:b2Body = b2Utils.createCircle(world, type, new b2Vec2, radius, density, friction, resititution, bodyIndex);
b2Utils.revoluteJoint(world, rect, circle, new b2Vec2, new b2Vec2, true, 3, 1000);

创建腿


腿由两个三角形组成,左右两边的腿是水平翻转的关系,所以先以右边的腿为例。上面的三角形为一个等边三角形,下面为锐角三角形,两个三角形有一个边平等且长度相同,并且在这条边的两上端分别有一个距离关节,用于固定和连接两个三角形,代码如下:


var legW:Number = 50;
var legH:Number = 75;
var legLen1:Number = 55;
var legLen2:Number = 55;
var legIndex:int = -2;

var v1:b2Vec2 = new b2Vec2;
var v2:b2Vec2 = new b2Vec2(0,-legW);
var v3:b2Vec2 = new b2Vec2(legW,0);

var v4:b2Vec2 = new b2Vec2;
var v5:b2Vec2 = new b2Vec2(legW,0);
var v6:b2Vec2 = new b2Vec2(0,legH);

//upper leg
var vertices1:Array = [v1, v2, v3];
var triangle1:b2Body = b2Utils.createPolygon(world, type, new b2Vec2, vertices1, density, friction, resititution, legIndex);
bodies.push(triangle1);

//lower leg
var vertices2:Array = [v4, v5, v6];
var triangle2:b2Body = b2Utils.createPolygon(world, type, new b2Vec2, vertices2, density, friction, resititution, legIndex);
bodies.push(triangle2);

//connect two legs
b2Utils.distanceJoint(world, triangle1, triangle2, new b2Vec2, new b2Vec2, legLen1);
b2Utils.distanceJoint(world, triangle1, triangle2, b2Math.MulMV(m, new b2Vec2(legW, 0)), b2Math.MulMV(m, new b2Vec2(legW, 0)), legLen2);

好了,右腿创建出来了,接下来的问题是,Box2D 可以水平翻转刚体吗?很遗憾,不能,没有相关的 API
干这个事。由于创建多边形点顺序的问题,一定要顺时针,所以并不能直接把点翻转过来。所以,创建左边的三角形需要改点东西,看代码:

var m:b2Mat22 = new b2Mat22();
m.Set(Math.PI);
var vertices1:Array = [v1, b2Math.MulMV(m, v3), v2];
var vertices2:Array = [v4, v6, b2Math.MulMV(m, v5)];

首先,保证点的顺序为顺时针,然后,通过 b2Math.MulMV 进行矩阵转换,将点旋转180度,这样创建出来的三角形就是左边的了。

组装

身体和腿都出来了,接下来就是把这两部分组装起来。首先,上面的三角形需要固定在身体的两侧,然后上面的三角形的上面的点利用距离关节接连到身体上的圆的边缘,最后,下面的三角度中间的点接连到圆上相同的位置,必须有两对以上的腿机器人才能站稳。

这里有一个问题,如果有2对腿,那连接到圆上的点应该是在圆的两侧,如果有3对腿,那每个点之间的圆角应该是 360 / 3,也就是 2 * PI /
3,如何找到圆上的这几个点呢?我试过3种方法,都是可行的,我最终选择了最简单的第3种方法:

1、知道半径,知道角度的情况下,用勾股定理算出来
2、找到第一个点,然后转换为圆的局部坐标,接着圆旋转 2 * PI /
n,再将局部坐标转换成全局坐标
3、找到第一个点,然后使用 b2Math.MulMV 转换

整合测试

点的转换的代码没给了,直接给出所有的代码,可以边调试边看。

package testbox2d
{
import Box2D.Common.Math.b2Vec2;
import Box2D.Dynamics.b2DebugDraw;
import Box2D.Dynamics.b2World;

import flash.display.Sprite;
import flash.events.Event;

import org.easily.box2d.b2Utils;
import org.easily.test.box2d.JansenWalker;

public class TestJansenWalker extends Sprite
{
private var world:b2World;
private var walker:JansenWalker;

public function TestJansenWalker()
{
super();

if (!stage)
{
addEventListener(Event.ADDED_TO_STAGE, createWorld);
}
else
{
createWorld(null);
}
}

private function createWorld(event:Event=null):void
{
var sleep:Boolean = true;
var gravity:b2Vec2 = new b2Vec2(0, 9.81);
world = new b2World(gravity, sleep);

createWalker();
b2Utils.createWall(world, stage.stageWidth, stage.stageHeight, 5, 2, 0.3, 0.5);
b2Utils.mouseJoint(stage, world);
b2Utils.createDebug(this, world, b2DebugDraw.e_jointBit | b2DebugDraw.e_shapeBit, 0.5);

addEventListener(Event.ENTER_FRAME, onUpdate);
}

private function createWalker():void
{
walker = new JansenWalker(world);
walker.setPosition(new b2Vec2(150, 350));
}

private function onUpdate(event:Event):void
{
world.Step(b2Utils.timeStep, b2Utils.velIterations, b2Utils.posIterations);
world.ClearForces();
world.DrawDebugData();
}
}
}

TestJansenWalker

package org.easily.test.box2d
{
import Box2D.Common.Math.b2Mat22;
import Box2D.Common.Math.b2Math;
import Box2D.Common.Math.b2Vec2;
import Box2D.Dynamics.b2Body;
import Box2D.Dynamics.b2FilterData;
import Box2D.Dynamics.b2World;

import org.easily.box2d.b2Utils;

/**
* jansenWalker by box2d
* @author Easily
*/
public class JansenWalker
{
private var world:b2World;
private var center:b2Body;
private var bodies:Array = [];

public function JansenWalker(world_:b2World)
{
world = world_;
createWalker();
}

public function setPosition(pos:b2Vec2):void
{
pos = pos.Copy();
pos.x /= b2Utils.worldScale;
pos.y /= b2Utils.worldScale;

var offset:b2Vec2 = center.GetPosition().Copy();
offset.Subtract(pos);

center.SetPosition(pos);
for each (var body:b2Body in bodies)
{
var old:b2Vec2 = body.GetPosition().Copy();
old.Subtract(offset);
body.SetPosition(old);
}
}

private function createWalker():void
{
var type:int = b2Body.b2_dynamicBody;
var density:Number = 2;
var friction:Number = 0.5;
var resititution:Number = 0.3;

var radius:Number = 28;
var size:b2Vec2 = new b2Vec2(45, radius / 2);

var legNum:Number = 4;

var legW:Number = 50;
var legH:Number = 75;
var legLen1:Number = 55;
var legLen2:Number = 55;

var jointLen1:Number = 88;
var jointLen2:Number = 85;

var bodyIndex:int = -1;
var legIndex:int = -2;

//motor
var rect:b2Body = b2Utils.createRect(world, type, new b2Vec2, size, density, friction, resititution, bodyIndex);
var circle:b2Body = b2Utils.createCircle(world, type, new b2Vec2, radius, density, friction, resititution, bodyIndex);
b2Utils.revoluteJoint(world, rect, circle, new b2Vec2, new b2Vec2, true, 3, 1000);
center = circle;
bodies.push(rect);

//legs
var average:Number = 2 * Math.PI / legNum;
var anchor:b2Vec2, first:b2Vec2 = new b2Vec2(radius * 2 / 3, 0);
var m:b2Mat22 = new b2Mat22();

var leftm:b2Mat22 = new b2Mat22();
var rightm:b2Mat22 = new b2Mat22();
leftm.Set(Math.PI);

for (var i:int = 0; i < legNum; i++)
{
m.Set(i * average);
anchor = b2Math.MulMV(m, first);
createLeg(false, leftm);
createLeg(true, rightm);
}

function createLeg(right:Boolean, m:b2Mat22):void
{
var v1:b2Vec2 = new b2Vec2;
var v2:b2Vec2 = new b2Vec2(0,-legW);
var v3:b2Vec2 = new b2Vec2(legW,0);

var v4:b2Vec2 = new b2Vec2;
var v5:b2Vec2 = new b2Vec2(legW,0);
var v6:b2Vec2 = new b2Vec2(0,legH);

//upper leg
var vertices1:Array = right ? [v1, v2, v3] : [v1, b2Math.MulMV(m, v3), v2];
var triangle1:b2Body = b2Utils.createPolygon(world, type, new b2Vec2, vertices1, density, friction, resititution, legIndex);
bodies.push(triangle1);

//lower leg
var vertices2:Array = right ? [v4, v5, v6] : [v4, v6, b2Math.MulMV(m, v5)];
var triangle2:b2Body = b2Utils.createPolygon(world, type, new b2Vec2, vertices2, density, friction, resititution, legIndex);
bodies.push(triangle2);

//connect two legs
b2Utils.distanceJoint(world, triangle1, triangle2, new b2Vec2, new b2Vec2, legLen1);
b2Utils.distanceJoint(world, triangle1, triangle2, b2Math.MulMV(m, new b2Vec2(legW, 0)), b2Math.MulMV(m, new b2Vec2(legW, 0)), legLen2);

//bind upper leg
b2Utils.revoluteJoint(world, rect, triangle1, b2Math.MulMV(m, new b2Vec2(size.x*3/2, 0)), new b2Vec2);

//join motor
b2Utils.distanceJoint(world, circle, triangle1, anchor, new b2Vec2(0, -legW), jointLen1);
b2Utils.distanceJoint(world, circle, triangle2, anchor, new b2Vec2, jointLen2);
}
}
}
}

JansenWalker

有好的方法,或者有意思的代码,请告诉我~

时间: 2024-10-10 15:08:04

使用 Box2D 做一个 JansenWalker 机器人的相关文章

应接不暇,应酬不断,不妨给自己的微信接一个聊天机器人

我是一名程序猿,我很忙,忙到我经常无法及时回复媳妇的微信,时常惹媳妇不开心.我既想跟媳妇开心地侃天侃地,又不敢贻误工作,进而影响公司产品的研发进度.难. 我是一名程序猿,产品经理经常在微信里提变态需求.心有不爽,我就会与他互怼.面对巧舌如簧的产品经理,我经常是词穷.我既无法说服他,又不敢与他大打出手.难. 我是一名程序猿,我不懂拒绝.地铁上,商场里,时常有人让我扫码加微信.于是,我加了许多莫名其妙的人,进了许多莫名其妙的群,看了许多莫名其妙的文章.每每陌生人跟我聊天,我既不想尴尬冷场,又无心思与

第一讲 从头开始做一个web qq 机器人,第一步获取smart qq二维码

新手教程: 前言:最近在看了一下很久很久以前做的qq机器人失效了,最近也在换工作目前还在职,时间很挺宽裕的.就决定从新搞一个web qq机器人 PC的协议解析出来有点费时间以后再做. 准备工作: 编译工具:vs2017 编程语言:C# 或者.net 开始建一个SDK  新建文件 -项目 -选择类库-(WEBQQSDK)-添加一个类 smartqq 第一步,登录了一下Smart QQ,以这个HTTP协议,做机器人交互吧,TX把这个版本的很多功能去掉了,基本的群聊,私聊 收发信息之类还在.(用谷歌浏

做一个“合格”的程序员

其实这篇文章很早就想写了,一直忙的没有时间,今天总算得空,下面就针对程序员这个职业来说一说我个人的一些想法: 要想做一个在我认为是"合格"的程序员,那么应该要做到以下几点: 代码规范,注释清楚 要做一个好的程序员,代码的质量是最重要的,代码是项目过程中最为重要的资源,有很多程序员觉得写注释太麻烦,还会花太多时间,尤其是很多规模比较小的公司,更不会注重这一点,但是往往到了项目后期,乃至项目由他人接手后,维护的成本会变得非常高,代码阅读困难,注释不详细甚至没有,维护人员需要靠自己去猜测某个

做一个“有资格”程序猿

其实这篇文章是很早就想写一,一直忙到没时间,今天终于是空的,继本职业工作方案谈猿我个人的一些想法: 要想做一个我觉得是"亲密格"程序猿.该要做到下面几点: 代码规范,凝视清楚 要做一个好的程序猿,代码的质量是最重要的,代码是项目过程中最为重要的资源.有非常多程序猿认为写凝视太麻烦,还会花太多时间,尤其是非常多规模比較小的公司,更不会注重这一点,可是往往到了项目后期.乃至项目由他人接手后,维护的成本会变得非常高,代码阅读困难,凝视不具体甚至没有.维护人员须要靠自己去推測某个方法的具体功能

笔记:利用 Cocos2dx 3.2 与 Box2D制作一个跑酷游戏

最近写lua写得没有力气了,所以想让脑袋放松一下,刚好看到有人在用swift做游戏: Swift游戏实战-跑酷熊猫 于是脑子一短路,就想到了利用这些素材来做一个游戏. 本来不想记笔记的,但是由于选择物理引擎的时候遇到诸多问题,所以选择记录下来,目前只做了个雏形,需要再完善一点. 需要知识: 1 cocos2dx-3.2 基本知识 2 box2d有一定的了解. 由于比较简单,所以把所有代码给上了先,然后再简单介绍下遇到的问题之类的东西. 首先是主角,熊猫类: Panda.h 1 #ifndef _

如何做一个优雅的人

--无论从事什么职业,我们首先是一个女人或者男人.女人应该像淑女,男人应该像绅士,愿所有的中国人都是淑女和绅士. --这里所说的文化.教养并不是大学生.硕士生或者博士生之类,而是一个人的行为举止中透露出来的素养,这是你们从书本上学不到的东西. --懂得标准,你就可以改变:不懂标准,你就没有信心. --人一开口,我们就知道他的内才有多少,所以,每个人都应该不断地丰富自己的内心世界. --如果你让他感觉到你很关心他,你真的很在乎他的病能不能好,那你就是一个很有修养的医生. 今天,我非常高兴有机会和你

初识Scrapy,在充满爬虫的世界里做一个好公民

欢迎来到你的Scrapy之旅.通过本文,我们旨在将你从一个只有很少经验甚至没有经验的Scrapy初学者,打造成拥有信心使用这个强大的框架从网络或者其他源爬取大数据集的Scrapy专家.本文将介绍Scrapy,并且告诉你一些可以用它实现的很棒的事情. 1.1 初识Scrapy Scrapy是一个健壮的网络框架,它可以从各种数据源中抓取数据.作为一个普通的网络用户,你会发现自己经常需要从网站上获取数据,使用类似Excel的电子表格程序进行浏览,以便离线访问数据或者执行计算.而作为一个开发者,你需要经

React-Native做一个文本输入框组件

我又回来啦! 由于最近一直在做公司的项目,而且比较急.如今项目已经迭代到第三期,可以缓一缓了... 今天在公司里听前端的说,这个项目本来是用React-Native做的(去年10月份),但是做到一半发现坑太多,就中途放弃了,于是让我们android和iOS重新开发... 作为非常喜欢这个技术的我来说,表示相当的不服. 于是我打算利用闲暇时间做一个一模一样的出来,反正接口我都有(嘻嘻) 说实话,最近一直再用android做开发,而且时间也不宽裕,react-native有点生疏了. 好了,废话不多

做一个手机端页面时,遇到了一个奇怪的问题:字体的显示大小,与在CSS中指定的大小不一致

最近在做一个手机端页面时,遇到了一个奇怪的问题:字体的显示大小,与在CSS中指定的大小不一致.大家可以查看这个Demo(记得打开Chrome DevTools). 就如上图所示,你可以发现,原本指定的字体大小是24px,但是最终计算出来的却是53px,看到这诡异的结果,我心中暗骂一句:这什么鬼! 随后开始对问题各种排查:某个标签引起的?某个CSS引起的?又或者是某句JS代码引起的.通过一坨坨的删代码,发现貌似都不是.我不禁又骂,到底什么鬼!不过中间还是发现了一些端倪:当页面中的标签数量或者文本数