Mission area forcefield
by Leslie Young · 06/03/2003 (9:37 am) · 6 comments
Download Code File
In this resource I'll show you how to create a cool force field type thing for your mission bounds. Robert Blanchet Jr. originally posted a resource, Mission Area Bounds, to render the bounds of the mission area, took this and modified it some and added detection for when the player try to leave the bounds and then bounce him back.
Let's get going, open up game.h add following before the #endif:
game.cc add, near the other includes:
find void GameRenderWorld() in game.cc
and then find this in there:
then add the following between the gClientSceneGraph->renderScene(); and glDisable(GL_DEPTH_TEST); ; change to look like this:
and finally add the GameRenderMissionArea function, anywhere in game.cc:
Right, all this should render the mission area with some texture, don't run it yet, cause it won't work :b
Now on to detecting the 'collision'. Open up player.h and find void checkMissionArea(); , and add, after it:
player.cc Find bool Player::updatePos(const F32 travelTime) and in there find:
and change it to look like this (added the else):
Now find void Player::checkMissionArea() and change the function like this:
And add the following function after the previous one. This function will allow the flash to appear on the client's machine too, the above changes were not enough. The flash also appears on the other clients too.
On to missionarea.h , make changes like this:
Right, in missionarea.cc add after RectI MissionArea::smMissionArea...
Add in MissionArea::MissionArea() :
In bool MissionArea::onAdd() just after setArea(mArea); before return(true) change like this:
then add the following functions (you can add 'em after the cSetArea function):
and finally the packupdates ... Change like this:
Compile, and hopefully I remembered to show you everything that need be changed 8-p
Now for some scripts:
Open up player.cs from server/scipts:
Find function Armor::onLeaveMissionArea(%this, %obj) and change like this:
and after that function add the following (this one is for when the player left the mission area via the roof, flight ceiling)
Another important thing is changes in the mission files, cause we added new fields in the code, and if you try loading the mission now it might not load, so check that your mission files has the following (for those that don't know, find your mission files in data/missions in fps, rw, or whatever path you are using). Open the .MIS (dot mis) files, in Tribal IDE or other text editor.
Look for something like this in there:
and change it like this:
k, that will do it, all you have to do now is add the bitmaps that will be used to render the border. I hard coded these but if you know what your doing it should be easy to change. Any case, create path like fps/data/borders and add 2 - 64x64 png - files one named border0.png and another called hit.png.
Few notes:
This code will render a border for the mission's flight ceiling too, you can rip that out if not needed
Explanation of the changes in your mission file:
texture=0; will tell the engine to render border0.png from the data/borders path , so if you want this mission to maybe use another image just add border1.png and use in your mission file texture=1;
shake=0.3; is the force at which the field will shake, play with it.
bounce=-2; is the force at which the player will be forced back when he hits the border.
There is a load of kewl things you could add, like start a particle effect when the player hit the border or play a sound effect, etc. I included a simple mission and some borders in the zip file.
Hope ya like :-)
In this resource I'll show you how to create a cool force field type thing for your mission bounds. Robert Blanchet Jr. originally posted a resource, Mission Area Bounds, to render the bounds of the mission area, took this and modified it some and added detection for when the player try to leave the bounds and then bounce him back.
Let's get going, open up game.h add following before the #endif:
void GameRenderMissionArea(F32 alpha, bool hit); // leslie
game.cc add, near the other includes:
#include "game/missionArea.h" #include <stdio.h>
find void GameRenderWorld() in game.cc
and then find this in there:
glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glClear(GL_DEPTH_BUFFER_BIT); glDisable(GL_CULL_FACE); glMatrixMode(GL_MODELVIEW); dglSetCanonicalState(); gClientSceneGraph->renderScene(); glDisable(GL_DEPTH_TEST); collisionTest.render(); #if defined(GATHER_METRICS) && GATHER_METRICS > 1
then add the following between the gClientSceneGraph->renderScene(); and glDisable(GL_DEPTH_TEST); ; change to look like this:
dglSetCanonicalState();
gClientSceneGraph->renderScene();
// ****************************************************************
// leslie - render mission area - start
S8 wasHit = 0;
bool hit = false;
static S32 timeOut = 0; // crude, but it works :)
wasHit = MissionArea::smWasHit;
MissionArea::smWasHit = 0;
if (wasHit>0) {
hit = true;
timeOut=200;
}
if (timeOut>0) hit = true; // hit from last time any case still on
timeOut--;
if (timeOut<0) timeOut=0;
// render normal border
GameRenderMissionArea(1.0f, false);
// render a flash over it if needed
if (hit) GameRenderMissionArea( (float)(timeOut/200.0f), true);
// leslie - end
// ****************************************************************
glDisable(GL_DEPTH_TEST);
collisionTest.render();and finally add the GameRenderMissionArea function, anywhere in game.cc:
// --------------------------------------------------------------------------------------------------
// leslie - render mission area - start
void GameRenderMissionArea(F32 alpha, bool hit)
{
F32 shakeFactor = MissionArea::smShakeFactor;
F32 height = MissionArea::smFlightCeiling;
S8 fn1 = MissionArea::smBorderTexNm;
TextureHandle bitMap = 0;
if (hit) {
bitMap = TextureHandle("mm/data/borders/hit.png", MeshTexture);
} else {
char buff[256]={'[[6288a01084065]]'};
sprintf(buff,"mm/data/borders/border%i.png",fn1);
bitMap = TextureHandle(buff, MeshTexture);
}
if (!bitMap) return;
TerrainBlock *terrain = gClientSceneGraph->getCurrentTerrain();
if(!terrain) return;
const RectI &area = MissionArea::smMissionArea;
Point2F min(area.point.x, area.point.y);
Point2F max(area.point.x + area.extent.x, area.point.y + area.extent.y);
// leslie : this will render the border a bit futher out sothat it doen't look like the
// player keft the misison area - as he does for a short moment and the player character
// will appear to be standing in the forcefield.
// if you mess with the player size you might wanna change these some
height += 1.0f;
min.x -= 1.0f; min.y -= 1.0f;
max.x += 1.0f; max.y += 1.0f;
dglClearBitmapModulation();
glDisable(GL_CULL_FACE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_LIGHTING);
glColor4f(1.0, 1.0, 1.0, alpha);
glEnable(GL_TEXTURE_2D);
TextureObject* texture = (TextureObject*)bitMap;
glBindTexture(GL_TEXTURE_2D, texture->texGLName);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
// side 1
glBegin(GL_TRIANGLE_FAN);
glTexCoord2f(0.0 +gRandGen.randF(0,shakeFactor),0.0 +gRandGen.randF(0,shakeFactor));
glVertex3f(min.x, min.y, 0);
glTexCoord2f((max.x - min.x)/5 +gRandGen.randF(0,shakeFactor),0 +gRandGen.randF(0,shakeFactor));
glVertex3f(max.x, min.y, 0);
glTexCoord2f((max.x - min.x)/5 +gRandGen.randF(0,shakeFactor), height/5 +gRandGen.randF(0,shakeFactor));
glVertex3f(max.x, min.y, height);
glTexCoord2f(0 +gRandGen.randF(0,shakeFactor),height/5 +gRandGen.randF(0,shakeFactor));
glVertex3f(min.x, min.y, height);
glEnd();
// side 3
glBegin(GL_TRIANGLE_FAN);
glTexCoord2f(0 +gRandGen.randF(0,shakeFactor),0 +gRandGen.randF(0,shakeFactor));
glVertex3f(min.x, max.y, 0);
glTexCoord2f((max.x - min.x)/5 +gRandGen.randF(0,shakeFactor),0 +gRandGen.randF(0,shakeFactor));
glVertex3f(max.x, max.y, 0);
glTexCoord2f((max.x - min.x)/5 +gRandGen.randF(0,shakeFactor),height/5 +gRandGen.randF(0,shakeFactor));
glVertex3f(max.x, max.y, height);
glTexCoord2f(0 +gRandGen.randF(0,shakeFactor),height/5 +gRandGen.randF(0,shakeFactor));
glVertex3f(min.x, max.y, height);
glEnd();
// side 0
glBegin(GL_TRIANGLE_FAN);
glTexCoord2f(0 +gRandGen.randF(0,shakeFactor),0 +gRandGen.randF(0,shakeFactor));
glVertex3f(min.x, min.y, 0);
glTexCoord2f((max.y-min.y)/5 +gRandGen.randF(0,shakeFactor),0 +gRandGen.randF(0,shakeFactor));
glVertex3f(min.x, max.y, 0);
glTexCoord2f((max.y-min.y)/5 +gRandGen.randF(0,shakeFactor),height/5 +gRandGen.randF(0,shakeFactor));
glVertex3f(min.x, max.y, height);
glTexCoord2f(0 +gRandGen.randF(0,shakeFactor),height/5 +gRandGen.randF(0,shakeFactor));
glVertex3f(min.x, min.y, height);
glEnd();
// side 2
glBegin(GL_TRIANGLE_FAN);
glTexCoord2f(0 +gRandGen.randF(0,shakeFactor),0 +gRandGen.randF(0,shakeFactor));
glVertex3f(max.x, min.y, 0);
glTexCoord2f((max.y-min.y)/5 +gRandGen.randF(0,shakeFactor),0 +gRandGen.randF(0,shakeFactor));
glVertex3f(max.x, max.y, 0);
glTexCoord2f((max.y-min.y)/5 +gRandGen.randF(0,shakeFactor),height/5 +gRandGen.randF(0,shakeFactor));
glVertex3f(max.x, max.y, height);
glTexCoord2f(0 +gRandGen.randF(0,shakeFactor),height/5 +gRandGen.randF(0,shakeFactor));
glVertex3f(max.x, min.y, height);
glEnd();
// side 5 ROOF
glBegin(GL_TRIANGLE_FAN);
glTexCoord2f(0 +gRandGen.randF(0,shakeFactor) ,0 +gRandGen.randF(0,shakeFactor));
glVertex3f(min.x, min.y, height);
glTexCoord2f((max.y-min.y)/5 +gRandGen.randF(0,shakeFactor), 0 +gRandGen.randF(0,shakeFactor));
glVertex3f(max.x, min.y, height);
glTexCoord2f((max.y-min.y)/5 +gRandGen.randF(0,shakeFactor), height/5 +gRandGen.randF(0,shakeFactor));
glVertex3f(max.x, max.y, height);
glTexCoord2f(0 +gRandGen.randF(0,shakeFactor), height/5 +gRandGen.randF(0,shakeFactor));
glVertex3f(min.x, max.y, height);
glEnd();
glDisable(GL_TEXTURE_2D);
glDisable(GL_BLEND);
}
// leslie - end
// --------------------------------------------------------------------------------------------------Right, all this should render the mission area with some texture, don't run it yet, cause it won't work :b
Now on to detecting the 'collision'. Open up player.h and find void checkMissionArea(); , and add, after it:
void checkGhostMissionArea();
player.cc Find bool Player::updatePos(const F32 travelTime) and in there find:
if (!isGhost()) {
// Collisions are only queued on the server and can be
// generated by either updateMove or updatePos
notifyCollision();
// Do mission area callbacks on the server as well
checkMissionArea();
}and change it to look like this (added the else):
if (!isGhost()) {
// Collisions are only queued on the server and can be
// generated by either updateMove or updatePos
notifyCollision();
// Do mission area callbacks on the server as well
checkMissionArea();
} else
checkGhostMissionArea(); // leslie - mission areaNow find void Player::checkMissionArea() and change the function like this:
void Player::checkMissionArea()
{
// Checks to see if the player is in the Mission Area...
Point3F pos;
MissionArea * obj = dynamic_cast<MissionArea*>(Sim::findObject("MissionArea"));
const RectI &area = obj->getArea();
getTransform().getColumn(3, &pos);
// leslie - mission area - roof collision - start
F32 height;
height = obj->getFlightCeiling();
if (pos.z >= height) {
if(mInMissionArea) {
MissionArea::smWasHit = 1; // leslie : this will alow flash on server too
mInMissionArea = false;
Con::executef(mDataBlock,3,"onLeaveMissionAreaViaRoof",scriptThis());
}
} else
// leslie - end
if ((pos.x < area.point.x || pos.x > area.point.x + area.extent.x ||
pos.y < area.point.y || pos.y > area.point.y + area.extent.y)) {
if(mInMissionArea) {
MissionArea::smWasHit = 1; // leslie : this will allow flash on server too
mInMissionArea = false;
Con::executef(mDataBlock,3,"onLeaveMissionArea",scriptThis());
}
}
else if(!mInMissionArea)
{
mInMissionArea = true;
Con::executef(mDataBlock,3,"onEnterMissionArea",scriptThis());
}
}And add the following function after the previous one. This function will allow the flash to appear on the client's machine too, the above changes were not enough. The flash also appears on the other clients too.
//----------------------------------------------------------------------------
// leslie - mission area check - start
void Player::checkGhostMissionArea()
{
// - this is to make this client make a flash on it's own mission bounds
// - aa, this is soo cool, it also seems to be updating on other clients
// - and the server when a client hits the border
// - LESLIE : NOTE : if it seems not to be working it might be a timimg
// - problem, cause it does work if a player left te mission area.
// Checks to see if the player is in the Mission Area...
Point3F pos;
F32 height;
const RectI &area = MissionArea::smMissionArea;
getTransform().getColumn(3, &pos);
height = MissionArea::smFlightCeiling;
if (pos.z >= height) {
MissionArea::smWasHit = 1;
}
if ((pos.x < area.point.x || pos.x > area.point.x + area.extent.x ||
pos.y < area.point.y || pos.y > area.point.y + area.extent.y)) {
MissionArea::smWasHit = 1;
}
}
// leslie - end
//----------------------------------------------------------------------------On to missionarea.h , make changes like this:
class MissionArea : public NetObject
{
private:
typedef NetObject Parent;
RectI mArea;
F32 mFlightCeiling;
F32 mFlightCeilingRange;
public:
MissionArea();
static RectI smMissionArea;
static F32 smFlightCeiling;// leslie
static F32 smShakeFactor; // leslie
static S8 smWasHit; // leslie
static S8 smBorderTexNm; // leslie
F32 mShakeFactor; // leslie
S8 mWasHit; // leslie
S8 mBorderTexNm; // leslie
static const MissionArea * getServerObject();
F32 getFlightCeiling() const { return mFlightCeiling; }
F32 getFlightCeilingRange() const { return mFlightCeilingRange; }
// leslie - mission area render effect support - start
void setBorderTexName(const S8 & s);
void setWasHit(const S8 & s);
void setShakeFactor(const F32 & f);
void setFlightCeiling(const F32 & f);
// leslie - end
//
const RectI & getArea(){return(mArea);
}
...Right, in missionarea.cc add after RectI MissionArea::smMissionArea...
IMPLEMENT_CO_NETOBJECT_V1(MissionArea); RectI MissionArea::smMissionArea(Point2I(768, 768), Point2I(512, 512)); F32 MissionArea::smFlightCeiling = 300.0f; // leslie F32 MissionArea::smShakeFactor = 0.7f; // leslie S8 MissionArea::smBorderTexNm = 0; // leslie S8 MissionArea::smWasHit = 0; // leslie
Add in MissionArea::MissionArea() :
mShakeFactor = 0.7f; // leslie mWasHit = 0; // leslie mBorderTexNm = 0; // leslie
In bool MissionArea::onAdd() just after setArea(mArea); before return(true) change like this:
if(!Parent::onAdd())
return(false);
setArea(mArea);
setShakeFactor(mShakeFactor); // leslie
setFlightCeiling(mFlightCeiling); // leslie
setBorderTexName(mBorderTexNm); // leslie
setWasHit(mWasHit); // leslie
return(true);then add the following functions (you can add 'em after the cSetArea function):
// --------------------------------------------------------------------------------------------------
// leslie - mission area render effect support - start
void MissionArea::setShakeFactor(const F32 & f)
{
mShakeFactor = MissionArea::smShakeFactor = f;
if(isServerObject()) mNetFlags.set(UpdateMask);
}
void MissionArea::setFlightCeiling(const F32 & f)
{
mFlightCeiling = MissionArea::smFlightCeiling = f;
if(isServerObject()) mNetFlags.set(UpdateMask);
}
void MissionArea::setBorderTexName(const S8 & s)
{
mBorderTexNm = MissionArea::smBorderTexNm = s;
if(isServerObject()) mNetFlags.set(UpdateMask);
}
void MissionArea::setWasHit(const S8 & s)
{
mWasHit = MissionArea::smWasHit = s;
if(isServerObject()) mNetFlags.set(UpdateMask);
}
// leslie - end
// --------------------------------------------------------------------------------------------------and finally the packupdates ... Change like this:
void MissionArea::unpackUpdate(NetConnection *, BitStream * stream)
{
// ghost (initial) and regular updates share flag..
if(stream->readFlag())
{
mathRead(*stream, &mArea);
stream->read(&mFlightCeiling);
stream->read(&mFlightCeilingRange);
stream->read(&mShakeFactor); // leslie
stream->read(&mBorderTexNm); // leslie
stream->read(&mWasHit); // leslie
}
}
U32 MissionArea::packUpdate(NetConnection *, U32 mask, BitStream * stream)
{
if(stream->writeFlag(mask & UpdateMask))
{
mathWrite(*stream, mArea);
stream->write(mFlightCeiling);
stream->write(mFlightCeilingRange);
stream->write(mShakeFactor); // leslie
stream->write(mBorderTexNm); // leslie
stream->write(mWasHit); // leslie
}
return(0);
}Compile, and hopefully I remembered to show you everything that need be changed 8-p
Now for some scripts:
Open up player.cs from server/scipts:
Find function Armor::onLeaveMissionArea(%this, %obj) and change like this:
function Armor::onLeaveMissionArea(%this, %obj)
{
// stop player from moving outside mission area
// get player pos and push 'em BACK!!!
%force = MissionArea.bounce;
// take this out and you can actually get out if
// you move forward slow enought
if (%obj.getVelocity()<3) %force -= 3;
// Thanx to Daniel Eden for helping me out with some code
// and finally this one line worked fine
%obj.setVelocity(VectorScale(%obj.getVelocity(), %force));
// leslie fixme : maybe start somekind of effect with particles and play
// a sound when collide with mission bounds
// ...
// inform the client
%obj.client.onLeaveMissionArea();
}and after that function add the following (this one is for when the player left the mission area via the roof, flight ceiling)
function Armor::onLeaveMissionAreaViaRoof(%this, %obj)
{
// stop player from moving outside mission area via roof
// get player pos and push 'em BACK!!!
%force = MissionArea.bounce;
if (%obj.getVelocity()<3) %force -= 3;
%obj.setVelocity(VectorScale(%obj.getVelocity(), %force));
// leslie fixme : maybe start somekind of effect with particles and play
// a sound when collide with mission bounds
// ...
// Inform the client
%obj.client.onLeaveMissionArea();
}Another important thing is changes in the mission files, cause we added new fields in the code, and if you try loading the mission now it might not load, so check that your mission files has the following (for those that don't know, find your mission files in data/missions in fps, rw, or whatever path you are using). Open the .MIS (dot mis) files, in Tribal IDE or other text editor.
Look for something like this in there:
new MissionArea(MissionArea) {
area = "-256 -256 256 256";
flightCeiling = "250";
flightCeilingRange = "20";
locked = "true";
};and change it like this:
new MissionArea(MissionArea) {
area = "-256 -256 256 256";
flightCeiling = "250";
flightCeilingRange = "20";
texture = "0"; // leslie
shake = "0.3"; // leslie
bounce = "-2"; // leslie
locked = "true";
};k, that will do it, all you have to do now is add the bitmaps that will be used to render the border. I hard coded these but if you know what your doing it should be easy to change. Any case, create path like fps/data/borders and add 2 - 64x64 png - files one named border0.png and another called hit.png.
Few notes:
This code will render a border for the mission's flight ceiling too, you can rip that out if not needed
Explanation of the changes in your mission file:
texture=0; will tell the engine to render border0.png from the data/borders path , so if you want this mission to maybe use another image just add border1.png and use in your mission file texture=1;
shake=0.3; is the force at which the field will shake, play with it.
bounce=-2; is the force at which the player will be forced back when he hits the border.
There is a load of kewl things you could add, like start a particle effect when the player hit the border or play a sound effect, etc. I included a simple mission and some borders in the zip file.
Hope ya like :-)
About the author
Recent Blogs
• TGEA GroundCover Terrain Surface Reference• Still around
• KBM update
• Finally some updates
• KBM.. More update
#2
These are the changes I made to the functions in server/scripts/player.cs
Now, MissionArea.bounce wouold contain a value like 10 for a gentle back movement or something like 50 or 60 to let 'em know ya don't want 'em near the borders :b
06/03/2003 (1:06 pm)
I've been playing with the pushback effect, here is another methid that's also quit usefull for pushing the player back ... not at an angle.These are the changes I made to the functions in server/scripts/player.cs
function Armor::onLeaveMissionArea(%this, %obj)
{
%obj.client.onLeaveMissionArea();
%force = MissionArea.bounce;
%min_x = getWord(MissionArea.area, 0);
%min_y = getWord(MissionArea.area, 1);
%max_x = %min_x + getWord(MissionArea.area, 2);
%max_y = %min_y + getWord(MissionArea.area, 3);
%x = getWord(%obj.getPosition(), 0);
%y = getWord(%obj.getPosition(), 1);
%vx = 0; %vy = 0; %vz = 0;
if (%x<(%min_x+10)) %vx = %force;
if (%y<(%min_y+10)) %vy = %force;
if (%x>(%max_x-10)) %vx = -%force;
if (%y>(%max_y-10)) %vy = -%force;
%vec = %vx @ " " @ %vy @ " " @ %vz;
%obj.setVelocity(%vec);
}
function Armor::onLeaveMissionAreaViaRoof(%this, %obj)
{
%obj.client.onLeaveMissionArea();
%force = MissionArea.bounce;
%min_x = getWord(MissionArea.area, 0);
%min_y = getWord(MissionArea.area, 1);
%max_x = %min_x + getWord(MissionArea.area, 2);
%max_y = %min_y + getWord(MissionArea.area, 3);
%x = getWord(%obj.getPosition(), 0);
%y = getWord(%obj.getPosition(), 1);
%vx = 0; %vy = 0; %vz = %force * -1;
if (%x<(%min_x+10)) %vx = %force;
if (%y<(%min_y+10)) %vy = %force;
if (%x>(%max_x-10)) %vx = -%force;
if (%y>(%max_y-10)) %vy = -%force;
%vec = %vx @ " " @ %vy @ " " @ %vz;
%obj.setVelocity(%vec);
}Now, MissionArea.bounce wouold contain a value like 10 for a gentle back movement or something like 50 or 60 to let 'em know ya don't want 'em near the borders :b
#3
Any chance you could package this up as a patch? Or at least provide the code as striaght text? Cutting and pasting out of HMTL doesn't work well at all.
06/12/2003 (4:57 pm)
Leslie,Any chance you could package this up as a patch? Or at least provide the code as striaght text? Cutting and pasting out of HMTL doesn't work well at all.
#4
But in the future I'll just include the original TXT files, I typed it in firts, in the zip too if size permit.
08/06/2003 (10:03 am)
Just paste into Word first and then to your code base :) (That is how I do it to get formatting sorted out)But in the future I'll just include the original TXT files, I typed it in firts, in the zip too if size permit.
#6
12/22/2006 (2:36 pm)

Associate Edward F. Maurina III
Roaming Gamer LLC