【发布时间】:2014-01-03 12:55:52
【问题描述】:
我正在研究类似于蛇的工作。我想做蛇身。
游戏逻辑是这样的:
蛇上下移动。移动应该像真正的蛇运动。
这里我被击中了。
如何制作蛇的身体?
任何想法或参考都应该对我有很大帮助。
提前致谢。
【问题讨论】:
我正在研究类似于蛇的工作。我想做蛇身。
游戏逻辑是这样的:
蛇上下移动。移动应该像真正的蛇运动。
这里我被击中了。
如何制作蛇的身体?
任何想法或参考都应该对我有很大帮助。
提前致谢。
【问题讨论】:
好的,这将是一个LONG答案。
我使用来自其他项目的一些代码和“蛇”部分整理了一个快速示例。你可以找到整个(cocos2d-x)代码库here on github。
最简单(也是首要)的事情是构建蛇身。从 Box2D 的角度来看,您可以将其构建为一系列段,每个段由一个旋转关节连接。
这是我们的目标图片:
这是我用来创建它的“粗略”代码:
// 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)。
当你看到它在运动时,它仍然不是完美,但我认为它看起来很适合几个小时的工作。
为了让蛇动起来,我采取了“拖动”它的头部的方法。当我拖动它时,头部会朝我的手指旋转(或者你可以让它跟随代码中的路径,追逐某物等)并将它的头部转向目标。身体的其余部分“拖动”,使其看起来“正常”。
控制器使用两种不同的机制来移动身体:
使用搜索行为的身体方向
body 使用“seek”行为,其工作原理如下:
以下是 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 m/s 向右移动 (currentVel) 并且您的最大速度 (desiredVel) 为 6 m/s,则推力将向右为正,将您更快地推向右侧 (desiredVel - currentVel = +1)。也就是说,你会向右加速。
如果您以 7 m/s 的速度向右 (currentVel) 移动,并且您的最大速度 (desiredVel) 为 6 m/s,则推力将为负,指向左侧,从而减慢您的速度 (desiredVel - currentVel = -1)。
从物理更新到更新,这使您的身体以您想要的速度朝着目标的方向移动。
在你的情况下,你只想左右移动,让重力把你的身体拉下来。所有这些都应该在物理学的背景下运作良好。您可以通过控制线性加速度来控制代理加速/减速的速度。
使用 PID 控制器进行身体旋转
这有点复杂(好像前一部分不是)。
这个想法是根据你想要去的角度和你的身体面对的角度之间的差异对身体施加扭矩。 PID 控制器确实使用当前角度差(乘以 比例 常数),最近历史的差(积分),以及角度差的变化率(导数)。前两个使身体转动,最后一个在达到目标角度时减慢速度。
You can see more details on the theory and implementation here.
所有这些都封装在代码中的一个类中,称为 PIDController。
注意:代码库中的 PIDController 是 generic。它对 box2d、物理或它的用途一无所知。这就像一个排序算法……它只关心它输入的数据和你为它的工作方式设置的参数。它可以轻松在其他情况下使用......并且已经使用了大约 100 年。
希望这能让你朝着正确的方向前进。这可能有点矫枉过正,但我有点挖掘这些东西,所以很有趣。
【讨论】:
您可以开始将蛇建模为 n 对齐的bodies(我猜circle shapes 会得到最好的结果),然后用distance joint 依次附加它们。
假设相机和经典的贪吃蛇游戏一样,你也应该将你的世界的gravity设置为0, 0。
【讨论】: