游戏逻辑是这样的:
蛇上下移动.移动应该像真正的蛇运动.
在这里,我受到了打击.
如何制作蛇的身体?
任何想法或参考应该对我有所帮助.
提前致谢.
好的,这将是一个很长的答案.我使用其他项目中的一些代码和“蛇”部分组合了一个快速示例.您可以找到整个(cocos2d-x)代码库here on github.
最容易(也是第一件)要做的就是建造蛇体.从Box2D的角度来看,你可以用一系列段来构建它们,每个段都由一个旋转关节连接起来.
>你想从单头开始
>然后使用相对于它的偏移进行迭代,从而创建分段
他们排成一列.
>在创建每个细分时,使用a将其链接到上一个细分
旋转关节.
>当你靠近尾巴时,开始逐渐减小身高.
以下是我们的目标:
这是我用来创建它的“粗略”代码:
// Constructor MovingEntity(b2World& world,const Vec2& position) : Entity(Entity::ET_MISSILE,10), _state(ST_IDLE) { // Create the body. b2BodyDef bodyDef; bodyDef.position = position; bodyDef.type = b2_dynamicBody; Body* body = world.CreateBody(&bodyDef); assert(body != NULL); // Store it in the base. Init(body); // Now attach fixtures to the body. FixtureDef fixtureDef; PolygonShape polyShape; vector<Vec2> vertices; const float32 VERT_SCALE = .5; fixtureDef.shape = &polyShape; fixtureDef.density = 1.0; fixtureDef.friction = 1.0; fixtureDef.isSensor = false; // Nose vertices.clear(); vertices.push_back(Vec2(4*VERT_SCALE,2*VERT_SCALE)); vertices.push_back(Vec2(4*VERT_SCALE,-2*VERT_SCALE)); vertices.push_back(Vec2(8*VERT_SCALE,-0.5*VERT_SCALE)); vertices.push_back(Vec2(8*VERT_SCALE,0.5*VERT_SCALE)); polyShape.Set(&vertices[0],vertices.size()); body->CreateFixture(&fixtureDef); body->SetLinearDamping(0.25); body->SetAngularDamping(0.25); // Main body vertices.clear(); vertices.push_back(Vec2(-4*VERT_SCALE,2*VERT_SCALE)); vertices.push_back(Vec2(-4*VERT_SCALE,-2*VERT_SCALE)); vertices.push_back(Vec2(4*VERT_SCALE,-2*VERT_SCALE)); vertices.push_back(Vec2(4*VERT_SCALE,2*VERT_SCALE)); polyShape.Set(&vertices[0],vertices.size()); body->CreateFixture(&fixtureDef); // NOW, create several duplicates of the "Main Body" fixture // but offset them from the previous one by a fixed amount and // overlap them a bit. const uint32 SNAKE_SEGMENTS = 4; Vec2 offset(-4*VERT_SCALE,0*VERT_SCALE); b2Body* pBodyA = body; b2Body* pBodyB = NULL; b2RevoluteJointDef revJointDef; revJointDef.collideConnected = false; // Add some "regular segments". for(int idx = 0; idx < SNAKE_SEGMENTS; idx++) { // Create a body for the next segment. bodyDef.position = pBodyA->GetPosition() + offset; pBodyB = world.CreateBody(&bodyDef); _segments.push_back(pBodyB); // Add some damping so body parts don't 'flop' around. pBodyB->SetLinearDamping(0.25); pBodyB->SetAngularDamping(0.25); // Offset the vertices for the fixture. for(int vidx = 0; vidx < vertices.size(); vidx++) { vertices[vidx] += offset; } // and create the fixture. polyShape.Set(&vertices[0],vertices.size()); pBodyB->CreateFixture(&fixtureDef); // Create a Revolute Joint at a position half way // between the two bodies. Vec2 midpoint = (pBodyA->GetPosition() + pBodyB->GetPosition()); revJointDef.Initialize(pBodyA, pBodyB, midpoint); world.CreateJoint(&revJointDef); // Update so the next time through the loop, we are // connecting the next body to the one we just // created. pBodyA = pBodyB; } // Make the next bunch of segments get "smaller" each time // to make a tail. for(int idx = 0; idx < SNAKE_SEGMENTS; idx++) { // Create a body for the next segment. bodyDef.position = pBodyA->GetPosition() + offset; pBodyB = world.CreateBody(&bodyDef); _segments.push_back(pBodyB); // Add some damping so body parts don't 'flop' around. pBodyB->SetLinearDamping(0.25); pBodyB->SetAngularDamping(0.25); // Offset the vertices for the fixture. for(int vidx = 0; vidx < vertices.size(); vidx++) { vertices[vidx] += offset; vertices[vidx].y *= 0.75; } // and create the fixture. polyShape.Set(&vertices[0],vertices.size()); pBodyB->CreateFixture(&fixtureDef); // Create a Revolute Joint at a position half way // between the two bodies. Vec2 midpoint = (pBodyA->GetPosition() + pBodyB->GetPosition()); revJointDef.Initialize(pBodyA, pBodyB, midpoint); world.CreateJoint(&revJointDef); // Update so the next time through the loop, we are // connecting the next body to the one we just // created. pBodyA = pBodyB; } // Give the tail some real "drag" so that it pulls the // body straight when it can. pBodyB->SetLinearDamping(1.5); pBodyB->SetAngularDamping(1.5); // Setup Parameters SetMaxAngularAcceleration(4*M_PI); // As long as this is high, they forces will be strong // enough to get the body close to the target position // very quickly so the entity does not "circle" the // point. SetMaxLinearAcceleration(100); SetMaxSpeed(10); SetMinSeekDistance(1.0); }
这将是你的第一部分,一个基本的身体.以下是有关代码的一些注意事项:
>我增加了身体的阻尼,甚至更多的尾巴,所以它可以
拖着并“拉下”剩下的链接.这使得
拖动它时看起来更平滑.
>相邻的身体部分不会碰撞,但非相邻的部分会发生碰撞,所以蛇
可以“击中”自己.您可以选择其他部件是否应该碰撞或
不.
现在,让身体按照你想要的方式移动会有点棘手.我先给你看一张图片(和视频),这样你就可以看到我得到的地方了.所有这些都在我引用的代码库中,因此如果您愿意,可以调整它.
首先,这是我将它拖了一下之后蛇的样子的截图.
我拍了一些视频,你可以看到它碰撞,减速等等(see it here).
当你看到它在运动中,它仍然不完美,但我认为它看起来相当不错的几个小时的工作.
为了使蛇移动,我采取了“拖动”它的头部的方法.当我拖动它时,头部朝我的手指旋转(或者你可以使它遵循代码中的路径,追逐某些东西等)并将其头部朝向目标.身体的其他部分“拖拽”,这使它看起来很“好”.
控制器使用两种不同的机制来移动身体:
使用寻求行为的身体方向
正文使用“搜索”行为,其行为如下:
下面是MovingEntity类的ApplyThrust(…)方法的代码.
void ApplyThrust() { // Get the distance to the target. Vec2 toTarget = GetTargetPos() - GetBody()->GetWorldCenter(); toTarget.Normalize(); Vec2 desiredVel = GetMaxSpeed()*toTarget; Vec2 currentVel = GetBody()->GetLinearVelocity(); Vec2 thrust = desiredVel - currentVel; GetBody()->ApplyForceToCenter(GetMaxLinearAcceleration()*thrust); }
该方法施加推力以使b2Body朝向目标方向移动.它具有最大速度(GetMaxSpeed())和最大线性加速度(GetMaxLinearAcceleration())作为类的属性.
如果您按照代码并绘制矢量,您将看到它的作用是施加推力来驱动您的速度,使其指向目标的位置.
另一种看待它的方法:它就像一个反馈循环(在矢量中),以保持你的速度与所需的速度匹配.如果您只是根据标量来考虑它,它会更容易看到.
如果你以5米/秒的速度向右移动(currentVel)并且你的最大速度(desiredVel)是6米/秒,那么推力将向右推,向右推得更快(desiredVel – currentVel = 1) .也就是说,你会加速到右边.
如果你以7米/秒的速度向右移动(currentVel)并且你的最大速度(desiredVel)是6米/秒,推力将为负,指向左侧,减慢你的速度(desiredVel – currentVel = -1) .
从物理的更新到更新,这使您的身体以您想要的速度朝目标方向移动.
在你的情况下,你只想向左和向右移动,让重力拉下你的身体.所有这一切都应该在物理学的背景下很好地发挥作用.您可以通过控制线性加速度来控制代理加速/减速的速度.
使用PID控制器进行车身旋转
这有点复杂(好像前一部分不是).
我们的想法是根据您想要的角度与您的身体所面对的角度之间的差异向身体施加扭矩. PID控制器使用当前角度差(乘以比例常数),最近历史的差值(积分)和角度差(变量)的变化率.前两个让身体转动,最后一个让它在达到目标角度时减速.
You can see more details on the theory and implementation here.
所有这些都封装在代码中的一个类中,称为PIDController.
注意:代码库中的PIDController是通用的.它对box2d,物理或它的用途一无所知.它就像一个排序算法……它只关心它被输入的数据以及你设置的参数如何工作.它可以很容易地在其他环境中使用……并且有大约100年的时间.
希望这能让你朝着正确的方向前进.这可能有点矫枉过正,但我有点挖掘这些东西,所以很有趣.