Making a Player Fly
by Mark Pumphrey · in Torque Game Engine · 07/15/2005 (3:31 am) · 125 replies
Firstly I would like to state that "Yeah, I have seen the posts about flying but....". They don't work and/or full of bugs. I would like to also state a general request that if you make a post regarding code to edit the original post instead continually trying to 'add' corrections later on making it harder than hell to read. Also, EVERYONE has a copy and paste function of some kind, use it. Saying that "here is some code than will change your life, insert it anywhere" means nothing and helps even less. Ok, enough of that. Has anyone actually made a player fly just like in Tribes?
#102
12/17/2007 (1:35 pm)
I just tried putting the function in the .cs file.
#103
Tell me all the steps you've taken. Also paste links to resources you've used. I'll copy what you did and see if I can get it working for you.
12/17/2007 (2:43 pm)
Ah cool - thanks for the typo correction. I'll update my code.Tell me all the steps you've taken. Also paste links to resources you've used. I'll copy what you did and see if I can get it working for you.
#104
12/17/2007 (5:18 pm)
Ok it's pretty long, most of it is going to be quoted:Quote:So I put it all together. I haven't tested this much, but I do know that flight tends to be a bit jittery. I'm not sure if it was always this way, if not I'll check it out. I didn't go to far to understand everything so I when you go out of flying mode it may stop doing something that the flying needs. Here goes.Next post...
find
// Acceleration due to gravity
VectorF acc(0,0,mGravity * mGravityMod * TickSec);
change it to
VectorF acc;//we have to create acc out here and then declare it inside the statements or else the compiler
//won't think that it exists
if(mFlying){
acc = VectorF(0.0f, 0.0f, 0.0f);
}else{
acc = VectorF(0.0f, 0.0f, mGravity * mGravityMod * TickSec);
}
Next scroll down just a little and find
// Acceleration on run surface
if (runSurface) {
mContactTimer = 0;
change it to
// Acceleration on run surface
if (runSurface||mFlying) {
if(mFlying)
mContactTimer = 30;
else
mContactTimer = 0;
Then scroll down a bit more and find
if (pvl) {
VectorF nn;
mCross(pv,VectorF(0,0,1),&nn);
nn *= 1 / pvl;
VectorF cv = contactNormal;
cv -= nn * mDot(nn,cv);
pv -= cv * mDot(pv,cv);
pvl = pv.len();
}
change it to
if (pvl&&!mFlying) {
VectorF nn;
mCross(pv,VectorF(0.0f, 0.0f, 1.0f),&nn);
nn *= 1.0f / pvl;
VectorF cv = contactNormal;
cv -= nn * mDot(nn,cv);
pv -= cv * mDot(pv,cv);
pvl = pv.len();
}
Edited on Dec 06, 2007 19:36
#105
12/17/2007 (5:19 pm)
Quote:I've been following Matt's initial progression for a bit here. All I have had to do thus far is add an if statement or modify and existing one. It gets a bit tougher, but I tried to make it as nice as possible. For those of you who looked at the code you may have wondered where mFlying came from, I will provide the code that defines that later. Anyways time for the big code block.
Scroll all the way back up to the top of Player::updateMove() and find
// Update current orientation
if (mDamageState == Enabled) {
F32 prevZRot = mRot.z;
delta.headVec = mHead;
F32 p = move->pitch;
if (p > M_PI) p -= M_2PI;
mHead.x = mClampF(mHead.x + p,mDataBlock->minLookAngle,
mDataBlock->maxLookAngle);
F32 y = move->yaw;
if (y > M_PI) y -= M_2PI;
GameConnection* con = getControllingClient();
if (move->freeLook && ((isMounted() && getMountNode() == 0) ||
(con && !con->isFirstPerson())))
{
mHead.z = mClampF(mHead.z + y,
-mDataBlock->maxFreelookAngle,
mDataBlock->maxFreelookAngle);
}
else
{
mRot.z += y;
// Rotate the head back to the front, center horizontal
// as well if we're controlling another object.
mHead.z *= 0.5;
if (mControlObject)
mHead.x *= 0.5;
}
// constrain the range of mRot.z
while (mRot.z < 0)
mRot.z += M_2PI;
while (mRot.z > M_2PI)
mRot.z -= M_2PI;
delta.rot = mRot;
delta.rotVec.x = delta.rotVec.y = 0;
delta.rotVec.z = prevZRot - mRot.z;
if (delta.rotVec.z > M_PI)
delta.rotVec.z -= M_2PI;
else if (delta.rotVec.z < -M_PI)
delta.rotVec.z += M_2PI;
delta.head = mHead;
delta.headVec -= mHead;
}
MatrixF zRot;
zRot.set(EulerF(0, 0, mRot.z));
// Desired move direction & speed
VectorF moveVec;
F32 moveSpeed;
if (mState == MoveState && mDamageState == Enabled)
{
zRot.getColumn(0,&moveVec);
moveVec *= move->x;
VectorF tv;
zRot.getColumn(1,&tv);
moveVec += tv * move->y;
// Clamp water movement
- continued in next post -
#106
12/17/2007 (5:21 pm)
Quote:- continue -
Now replace that code block with
// Update current orientation
if (mDamageState == Enabled) {
Point3F prevRot = mRot;
delta.headVec = mHead;
F32 p = move->pitch;
if (p > M_PI_F)
p -= M_2PI_F;
mHead.x = mClampF(mHead.x + p,mDataBlock->minLookAngle,
mDataBlock->maxLookAngle);
F32 y = move->yaw;
if (y > M_PI_F)
y -= M_2PI_F;
GameConnection* con = getControllingClient();
if (move->freeLook && ((isMounted() && getMountNode() == 0) || (con && !con->isFirstPerson())))
{
mHead.z = mClampF(mHead.z + y,
-mDataBlock->maxFreelookAngle,
mDataBlock->maxFreelookAngle);
}
else
{
mRot.x += p;
mRot.z += y;
// Rotate the head back to the front, center horizontal
// as well if we're controlling another object.
mHead.z *= 0.5f;
if (mControlObject)
mHead.x *= 0.5f;
}
// constrain the range of mRot.z
while (mRot.z < 0.0f)
mRot.z += M_2PI_F;
while (mRot.z > M_2PI_F)
mRot.z -= M_2PI_F;
// constrain the range of mRot.x
if(mRot.x > 1.5706)
mRot.x = 1.5706;
else if(mRot.x < -1.5706)
mRot.x = -1.5706
delta.rot = mRot;
delta.rotVec = prevRot - mRot;
if (delta.rotVec.z > M_PI_F)
delta.rotVec.z -= M_2PI_F;
else if (delta.rotVec.z < -M_PI_F)
delta.rotVec.z += M_2PI_F;
delta.head = mHead;
delta.headVec -= mHead;
}
MatrixF zRot;
zRot.set(EulerF(0.0f, 0.0f, mRot.z));
if(mFlying){
MatrixF xRot;
xRot.set(EulerF(mRot.x, 0, 0));
zRot.mul(zRot, xRot); //well its not zrot anymore but the code is a bit more efficient this way
}
// Desired move direction & speed
VectorF moveVec;
F32 moveSpeed;
if (mState == MoveState && mDamageState == Enabled)
{
zRot.getColumn(0,&moveVec); //note that if you are flying its not zRot it is xzRot
moveVec *= move->x;
VectorF tv;
zRot.getColumn(1,&tv);
moveVec += tv * move->y;
if(mFlying){
zRot.getColumn(2,&tv);
moveVec += tv * move->z;
}
// Clamp water movement
well if I made any mistakes that is probably where they are. It does work though and I was surprised with how clean the code worked out to be.
#107
12/17/2007 (5:21 pm)
Quote:(I'm just going to copy what Matt said here because it works)
Now if you are like me and think that the falling animation looks better for flying around then jump over to Player::pickActionAnimation() and scroll down till you find:
if (mContactTimer >= sContactTickTime) {
// Nothing under our feet
action = PlayerData::RootAnim;
}
change it to
if (mContactTimer >= sContactTickTime) {
// Nothing under our feet
if(mFlying)
action = PlayerData::FallAnim;
else
action = PlayerData::RootAnim;
}
Well that's where Matt left off but I get to keep going, hopefully I remember it all.
So lets go back up near the top to 'Player::Player()' under
mState = MoveState;
mFalling = false;
put
mFlying = false;
find
ConsoleMethod( Player, getState, const char*, 2, 2, "Return the current state name.")
{
return object->getStateName();
}
above it put
void Player::setFlying(bool Flying){
mFlying = Flying;
}
bool Player::getFlying(){
return mFlying;
}
//----------------------------------------------------------------------------
ConsoleMethod( Player, toggleFlying, bool, 2, 3, "(bool Flying)")
{
bool Flying = (argc > 2)? dAtob(argv[2]): !object->getFlying();
object->setFlying(Flying);
return 1;
}
I'm not sure if that's the "right" place but it works as well as any.
Now we are moving on over to player.h
under
ActionState mState; ///< What is the player doing? @see ActionState
bool mFalling; ///< Falling in mid-air?
put
bool mFlying; ///< Should we take off towards the sun?
a little bit under that find
static void consoleInit();
under it add
void setFlying(bool Flying);
bool getFlying();
Well I think that's all done. I didn't add Tom Spilman's change in this list but you should probably scroll up and add it, and make sure to add the onread packet stuff as well.
#108
12/17/2007 (5:23 pm)
Quote:in void Player::readPacketData(GameConnection *connection, BitStream *stream)
if (stream->readFlag()) {
// Only written if we are not mounted
stream->read(&pos.x);
stream->read(&pos.y);
stream->read(&pos.z);
stream->read(&mVelocity.x);
stream->read(&mVelocity.y);
stream->read(&mVelocity.z);
stream->setCompressionPoint(pos);
delta.pos = pos;
mJumpSurfaceLastContact = stream->readInt(4);
}
else
pos = delta.pos;
stream->read(&mHead.x);
stream->read(&mHead.z);
stream->read(&rot.x);
stream->read(&rot.y);
stream->read(&rot.z);
stream->read(&mFlying);
then in void Player::writePacketData(GameConnection *connection, BitStream *stream)...
if (stream->writeFlag(!isMounted())) {
// Will get position from mount
stream->setCompressionPoint(pos);
stream->write(pos.x);
stream->write(pos.y);
stream->write(pos.z);
stream->write(mVelocity.x);
stream->write(mVelocity.y);
stream->write(mVelocity.z);
stream->writeInt(mJumpSurfaceLastContact > 15 ? 15 : mJumpSurfaceLastContact, 4);
}
stream->write(mHead.x);
stream->write(mHead.z);
stream->write(mRot.x);
stream->write(mRot.y);
stream->write(mRot.z);
stream->write(mFlying);
for the pack/unpackUpdate change to this...
in void Player::unpackUpdate(NetConnection *con, BitStream *stream)...
if(stream->readFlag())
{
stream->readNormalVector(&mVelocity, 10);
mVelocity *= stream->readInt(13) / 32.0f;
}
else
{
mVelocity.set(0.0f, 0.0f, 0.0f);
}
//rot.y = rot.x = 0.0f;
rot.x = stream->readFloat(7) * M_2PI_F;
rot.y = stream->readFloat(7) * M_2PI_F;
rot.z = stream->readFloat(7) * M_2PI_F;
mHead.x = stream->readSignedFloat(6) * mDataBlock->maxLookAngle;
mHead.z = stream->readSignedFloat(6) * mDataBlock->maxLookAngle;
mFlying = stream->readFlag();
delta.move.unpack(stream);
then in U32 Player::packUpdate(NetConnection *con, U32 mask, BitStream *stream)...
if(stream->writeFlag(len > 0.02f))
{
Point3F outVel = mVelocity;
outVel *= 1.0f/len;
stream->writeNormalVector(outVel, 10);
len *= 32.0f; // 5 bits of fraction
if(len > 8191)
len = 8191;
stream->writeInt((S32)len, 13);
}
stream->writeFloat(mRot.x / M_2PI_F, 7);
stream->writeFloat(mRot.y / M_2PI_F, 7);
stream->writeFloat(mRot.z / M_2PI_F, 7);
stream->writeSignedFloat(mHead.x / mDataBlock->maxLookAngle, 6);
stream->writeSignedFloat(mHead.z / mDataBlock->maxLookAngle, 6);
stream->writeFlag(mFlying);
delta.move.pack(stream);
NOTE! You must write and read data in the same order other wise the data gets swapped around
Also, I haven't tested the code above but in theory it works - the concept is to pack the data and write the data in the correct order on the server and then unpack and read the data on the client
It would cool if someone could check this for me.
@Tyler - if you still can't get it to work I will run the update myself as I have a vampire that I want to fly, and will show you how I did it.
EDIT : changed line
stream->readFlag(mFlying);
to...
stream->writeFlag(mFlying);
Edited on Dec 17, 2007 17:44
#109
and add:
Do the same in the read section.
12/17/2007 (5:28 pm)
Find stream->write(mHead.x); stream->write(mHead.z); stream->write(mRot.x);
and add:
stream ->write(mRot.y); stream ->write(mRot.z); stream ->write(mFlying);
Do the same in the read section.
#110
and put this in config.cs (default.bind.cs for tutorial.base)
config.cs:
Press t to start flying!
12/17/2007 (5:32 pm)
Now, compile it and put this into your server folder:function serverCmdtoggleFlying(%client)
{
%client.player.setTransform(LocalClientConnection.player.getTransform());
if (isObject(%client.player))
%client.player.toggleFlying();
}and put this in config.cs (default.bind.cs for tutorial.base)
config.cs:
moveMap.bindCmd(keyboard, "t", "commandToServer(\'toggleFly\');", "");default.bind.cs:
GlobalActionMap.bindCmd(keyboard, "t", "commandToServer(\'toggleFly\');", "");
Press t to start flying!
#111
EDIT: Is it against the law for me to put this as a resource? I didn't really make the whole thing so that's the problem.
12/17/2007 (5:34 pm)
Hey I think I'm gonna make this a resource since no one has bothered.EDIT: Is it against the law for me to put this as a resource? I didn't really make the whole thing so that's the problem.
#112
12/17/2007 (10:56 pm)
Please do make this a resource. I'm sure nobody would mind you making this into a resource. Just acknowledge others in the resource and link this thread. It should be fine.
#113
12/18/2007 (2:27 am)
@Tyler - so did you get it to work in the end?
#114
12/18/2007 (9:53 am)
Everything is working except the GUI button.
#115
12/18/2007 (5:03 pm)
How do I get a button to call a server cmd?
#116
12/18/2007 (5:19 pm)
I just typed serverCmdtoggleFlying in the console. It did the setTransform thing, but not the important toggleFlying.
#117
12/19/2007 (5:46 am)
I think the actual command might just be toggleFlying without serverCmd at the beginning. It might also be that the actual function needs to be in a server script and is defined as just function toggleFlying() and you call it from the client by using serverCmdtoggleFlying - the serverCmd eliminates name space clashes with identical functions on the client - though it's all just guessing at this point.
#118
12/19/2007 (6:49 pm)
Not toggleFlying. Although it seems that if I do serverCmdtoggleFlying(); for the button command, it does the setTransform part, but it won't toggle the flying.
#119
Then have the GUI button's command be:
It works perfectly!
12/19/2007 (6:54 pm)
I just go it to work! You need to make a function that calls that function like:function serverCmdcallFlying(%client)
{
%client.player.setTransform(LocalClientConnection.player.getTransform());
serverCmdactivateFlying();
}
funtion serverCmdactivateFlying(%client)
{
if (isObject(%client.player))
%client.player.toggleFlying();
}Then have the GUI button's command be:
command = serverCmdcallFlying();
It works perfectly!
#120
12/20/2007 (1:14 am)
Nice work Tyler! - I hope you compile this into a resource. I'm going to give it a go soon.
Torque Owner Tyler Slabinski
This is what I have in the playGui.gui:
//--- OBJECT WRITE BEGIN --- new GameTSCtrl(PlayGui) { canSaveDynamicFields = "1"; Profile = "GuiContentProfile"; HorizSizing = "right"; VertSizing = "bottom"; position = "0 0"; Extent = "1024 768"; MinExtent = "8 8"; canSave = "1"; Visible = "1"; hovertime = "1000"; applyFilterToChildren = "1"; cameraZRot = "0"; forceFOV = "0"; noCursor = "0"; helpTag = "0"; new GuiButtonCtrl() { canSaveDynamicFields = "0"; Profile = "GuiButtonProfile"; HorizSizing = "right"; VertSizing = "bottom"; position = "150 425"; Extent = "140 30"; MinExtent = "8 2"; canSave = "1"; Visible = "1"; command = "toggleFlying();"; tooltipprofile = "GuiDefaultProfile"; ToolTip = "Press this button to toggle flying"; hovertime = "1000"; text = "Toggle Flying"; groupNum = "-1"; buttonType = "PushButton"; }; }; //--- OBJECT WRITE END --- function toggleFlying() { serverCmdtoggleFly(); }I don't see anything wrong with it though.