Gravgun
WORK IN PROGRESSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS!!!!!!!!!!!!!!!!!!!!!
I am OrigHammer from www.masteringunreal.com. I am the creator of the GravGun for Unreal 2004, it is not finished but I am going to start writing a tutorial. The gun can be found in the forums at http://sv3.3dbuzz.com/vbforum/showthread.php?s=&threadid=73788.
This gun if focused around the linkgun but changes much of its functionality.
It all started off as a simple project but turned into something more. The gun is not bug free but this experience has taught me a lot about unrealscript so I figured I would share my experiences, both bad and good.
This gun grabs objects and then can throw them. Eg. (Throwing a player or picking up a barrel and throwing it at a player)
Before I jump into things, what I originally wanted was a gun to pick things up and throw them, thats all. The only gun that I could think of was the linkgun because it shot out a beam so I figure I could attach objects to the beam. Over the course of the gun I changed things and totally rewrote code, I will take you through both the old and new code so you too can learn from mistakes.
So here goes:
- Classes that are edited:
- Editing LinkFire.uc (The heart of the gun)
- Editing LinkGun.uc
- Editing LinkAltFire.uc
- Editing LinkBeamEffect.uc
Heres how its gonna work. Im gonna take you through my thought process (even if it was wrong), then I will tell you the better way of doing things. At first there was so much code and I was scared, now I understand a lot lot more, so just hang in there.
LinkFire.uc
When first looking at LinkFire.uc, (Dont be scared, I know its fairly large), I wanted to find where the player was damaged, because if I could find out where the player gets hit, then I could attach the player (or object) to the beam instead of creating damage. The area of code im talking about is in the function ModeTick, here it is:
// beam is updated every frame, but damage is only done based on the firing rate if ( bDoHit ) //bDoHit just means the beam hit something { if ( Beam != None ) //self explanitory Beam.bLockedOn = false; //the Beam is not locked onto another player or vehicle Instigator.MakeNoise(1.0); AdjustedDamage = AdjustLinkDamage( LinkGun, Other, Damage ); if ( !Other.bWorldGeometry ) //This says that the beam did not hit world geometry { //This if statement just makes sure if the beam his a player on your own team then they dont get hurt if ( Level.Game.bTeamGame && Pawn(Other) != None && Pawn(Other).PlayerReplicationInfo != None && Pawn(Other).PlayerReplicationInfo.Team == Instigator.PlayerReplicationInfo.Team) AdjustedDamage = 0; HealObjective = DestroyableObjective(Other); if ( HealObjective == None ) HealObjective = DestroyableObjective(Other.Owner); if ( HealObjective != None && HealObjective.TeamLink(Instigator.GetTeamNum()) ) { SetLinkTo(None); bIsHealingObjective = true; if (!HealObjective.HealDamage(AdjustedDamage, Instigator.Controller, DamageType)) LinkGun.ConsumeAmmo(ThisModeNum, -AmmoPerFire); } else Other.TakeDamage(AdjustedDamage, Instigator, HitLocation, MomentumTransfer*X, DamageType); if ( Beam != None ) Beam.bLockedOn = true; }
SO WHAT THE HELL DOES THIS MEAN????
Well to start off remember we are looking for where the player gets hurt. Hey whats that I notice the line Other.Takedamage(AdjustedDamage, Instigator, HitLocation, MomentumTransfer*X, DamageType); right near the bottom. Well looks like we found what we are looking for. At least let me help you with the other part though. So I added comments in the code to help. Remember, ask and ill explain as best I can.
So we found where the player gets hurt. What do we do next. From looking through code and the Actor functions described at the Unreal Devolopers Network I found a function called SetBase. It was exactly what I wanted to attach the beam to the player or object right? WRONG! The problem was this, the beam is made of particles. Particles move and particles dye. So by doing, Beam.SetBase(Other);, I could pick things up but if I moved quickly they would drop because the particle that was attached to the player was either dying or moving. I realized that a better way to move around objects was to use either SetLocation or Move / MoveSmooth functions. The functions are similar yet very very different. The setlocation function takes a vector and moves and object instantaneously to that position. The move function moves an object in the direction of the vector you give it. So how do I move an object around?
Weapon.GetViewAxes(A,C,D); Other.MoveSmooth((Instigator.Location + 250*A + vect(0,0,50)) - Other.Location);
I got rid of setbase and used MoveSmooth because the UDN (unreal developers network) says that movesmooth is faster, even though it was harder to use. This always moves the object towards the crosshair. If I used SetLocation I would have just done:
Weapon.GetViewAxes(A,C,D); Other.SetLocation((Instigator.Location + 250*A + vect(0,0,50)));
Now let me tell you what those actually mean. Instigator.Location is just the location of the player you control. A, C, and D are vectors but the only one we are interested in is A. It will be a unit vector that points in the direction of the crosshair (where the player is looking). 250*A is the distance we want the object from us. The bigger the number the further away. I add 50 to the z-axis so that the object is around eye level. There is an actual EyeHeight variable I could have used but I did not know about it then. Other.Location is a vector of where the object we are holding is. The two are very close in terms of code, yet very different in what they do. The code in MoveSmooth makes the object move towards the crosshair. SetLocation just makes the object be at the location of the crosshair, but it updates so quickly that it looks like its moving, not just changing positions.
You may wonder why I did not use Instigator.Rotation or Weapon.Rotation to get the direction that the player is looking. The reason for this is simply because it wont work. A rotator has 3 components, Yaw, Pitch and Roll. The problem is that a player running around and looking around has a Yaw and a Pitch, but not a Roll. Therefore when converting the rotator to a vector the Z-axis part of the vector will always be 0, NOT what I want. (This is what I think to be correct at least, so correct me if im wrong).
But when we try to pick something up nothing happens, well thats because we have to worry about physics. Ill tell you right away that the Karma physics used in the unreal engine are flawed and that they suck compared to quality physics engines out there, such as the Hovok engine that will be used in the game Half Life 2 and many others.
You can set the physics of an object to be a wide variety of things. (Listed in the Actor class. Search for PHYS_) So, when I wanted to grab an object I set its physics to none by doing, Other.SetPhysics(PHYS_None);(set the physics of the object to nothing), but if I moved around quickly then all of a sudden the object was just floating in space. Or at least is was until I stopped using the SetBase function. On top of that the object will go right through walls. As a quick side note, the Unreal engine is really the wrong engine to be making this gun because the PHYSICS SUCK. In the Karma physics documentation they even admit that the physics are very limited and there are problems that have not been fixed. But back to how to stop putting objects going through walls. I discovered a variable in the actor class called bCollideWorld, I set that sucker to true and things stopped going through walls. If you played UT2004 you would know that this does not work perfectly because vehicles can get stuck or sink in world geometry. BLAME KARMA. (I know im bashing them a lot but in the next unreal engine, Unreal Engine 3, they are using a newer physics engine.)