3/04 | 4/04 | <-Open Closed-> Last Free Code | 5/04 | 6/04 | Most Recent


1 April '04
- Today is the day. I swear it. I WILL get this frigging thing working both sides. So.. moved all vars from the PC to the Pawn. That made things a lot easier, cos now only the pawn holds gesture related variables. Good. The mutator assigns everything through checkReplacement(), since this way is much much less annoying. Good. Added some checks for bots, and stopped giving them the gesture gun so there aren't any more annoying warning messages. Good. Now we move on..
- Well, I got stuck / bored again, so instead i fiddled with the camera on the sword. It has some interpolation on it now, and looks pretty cool. However, i can't take much credit since pretty much the entire code block was ripped from the KVehicle class. - OH I ALMOST FORGOT!! - I made a dumb little mutator when i lost interest in this. It makes onslaught pretty awesome fun. HERE - no wait. here.

4 April '04
- Well, i've been pretty inactive the last few days with this thing, but now it's time for a *HUGE* update. And guess what? Absolutely EVERYTHING works properly with gesture effects now. I'm not even kidding. I'll try to go over that code but there will doubtlessly be parts i don't really remember. There's a link to the full source at the bottom of today's post.
The most important change readability-wise i guess is that all the gesture functionality has been moved over to its own class. This class is your standard inventory extension, loaded up through the pawn by the mutator. In other words, the mutator says:

 function ModifyPlayer(Pawn Other)
{
    
Other.GiveWeapon("grabin.sword");
    if (
Other.Controller.isA('grabinController'))    //no bot gestures yet
    
{
        
Other.GiveWeapon("grabin.grabinGestureGun");
        
grabinPawn(Other).GiveGesture("grabin.gestureInventory");
    }


paying close attention to the GiveGesture() call here. This function exists in the pawn class and does the following:

 static function GiveGesture(string aClassName )
{
    
local class<gestureInventoryInvClass;
    
    
InvClass = class<gestureInventory>(DynamicLoadObject(aClassName, class'Class'));
    if( 
FindInventoryType(InvClass) != None )
        return;
    
GestureInv Spawn(InvClass);
    if( 
GestureInv != None )
    {
       
GestureInv.GiveTo(self);
       
GestureInv.self;  //assignment so that inv class can reference back easily
       
GestureInv.SetupGesture();
    }


Basically all that stuff just lets me spawn whatever the hell i want and have it attached automatically to the pawn. The pawn's replication block becomes extremely important now, as this function must execute client-side only to not cause problems. To make it a replicated function, all we need is:

 reliable if (ROLE == ROLE_Authority)
        
giveGesture


Now the most important class of all, the inventory class which handles all gesturing. It's basically just full of the functions I had lying around everywhere before in the Controller and Pawn classes, but there is one very important function when it comes to detecting the correct interaction...

 simulated function getInteraction()
{
    
local PlayerController PCn;
    
local int i//iterator
    
    
foreach DynamicActors(class'PlayerController'PCn)
    {
        if ( 
Viewport(PCn.Player) != None )
            {
                for (
0PCn.Player.LocalInteractions.Lengthi++)
                    {
                        if ( 
PCn.Player.LocalInteractions[i].IsA('gestureInter') )
                            
gInteraction PCn.Player.LocalInteractions[i];
                    }
                }
        }


I think its fairly self-explanatory how that works- iterate through all actors until you find the right PC, get its player and make sure it is indeed, the current viewport (which as far as i can tell means the thing the player is looking through), then iterate through all its interactions until you find the right one. Assign that and everything should be ok. This algorithm is pretty crappy really when you think about complexity.. probably (O)n� or whatever, i can't remember how it works.. BUT it doesnt really matter since this function need only be executed once.
The next thing i had to do was move all the functions to actually draw gesturing effects over to a PlayerReplicationInfo class. I think the problem there was something to do with replication inheritance.. since beforehand i was trying to call the functions from a timer, and they were both in the same class. I think the timer's replication (none at all, i mean whod want messages being sent every tick?) overrode any other replication i layed down for each of the effect drawing functions. Anyway, i moved them to a PRI and just call them now, and everything seems to work just fine. I will have to smooth things out a little later though, maybe have a buffer of places to spawn the emitters which gets passed to the server every now and again, because right now client effects are much less full-bodied than server ones due to lag. And that is, in a nutshell, all that was needed to make the gesture effects show up properly for everyone.
- What else? Ummm.. The sword deals damage properly now and has a lot of recoil.. i think that's about all for today really (:
Full source code for this stage.

5 April '04
- The sword now has a proper crosshair, which is implemented as an Emitter following the player's gaze by a simple trace.

6 April '04
- I haven't been doing much lately, I must admit. The next thing i do will either be to get some gesture recognition implemented or do the proper sword animations. Animating with code impulses is definitely not recommended. I will get to these things in the next few days maybe, but I have a lot of assignments due so probably not. Also, I have to get drunk on my birthday. yes.
In the meantime, here's something to keep you amused -- Linkgun Grappling Hook.

13 April '04
- Started coding the gesture calculation functions into gestureInventory and grabinPRI. If you haven't worked it out yet, gestureInventory contains all the client-side code and the PRI contains the server-side stuff. This is a pretty handy way of doing things, i think, because all the hard computation can be done by clients and then passed to the server when something BIG (yet low on computation) needs to happen (ie the gesture has some effect).
- To fix sounds not playing properly, i moved those functions over to the PRI class so all players can hear them. One function to play a release sound (which i may extend later to play different sounds for each gesture type), and one to play the ambient sound of a gesture happening:

 simulated function playRelease(pawn Other)
//chooses a casting sound and plays it
    
Other.PlaySound(Sound'gestureFX.cast'SLOT_None,255,,128,,true);
}

simulated function playAmbient(pawn Otherbool on)
//sets the ambient effect for gesturing
    
if (on)
        
Other.AmbientSound Sound'gestureFX.gesture';
    else
        
Other.AmbientSound None;


- Now comes perhaps the most important coding step yet.
I should explain the mechanics of my gesture recognition algorithm so that others will know how to implement it in other object classes, so that external entities in custom maps or whatever can be controlled by gestures. I'm going to copy some documentation from the gesturing state first, the state which drives all gesture calculation and does the calls for the effect display. To explain a little about states: States are an important part of game programming and really make many things much easier. Basically, an object in state 'x' only executes code from 'x', not from any other states. This means that an object can have completely different behaviour in different states. In most games, 'walking' and 'swimming', for example, are two different player states. The Unreal engine has some really powerful state functions, such as beginState() and endState(), which funnily enough execute only when the state is entered and exited. There are also functions to check what state an actor is in and change its state. I'll explain later anyway. The 'gesturing' state i have created in gestureInventory:
	//====================================================================
	// is responsible for calculating the gesture ID array to 
	// be passed to the PRI to see what happens. the array generated
	// takes this form:
	//	INDEX		NUMBER
	//	0		0	(zero indicates first pen down)
	//	1		First directional number
	//	...		Second one... etc
	//	...		0	This means the 'pen' was lifted and placed again
	//	...		More numbers for the next shape drawn
	//	Length-1	Last number in last gesture
	//====================================================================

As you can see, I have implemented the gesture ID number as an array of integer. It works like this:
		// rotator notes:
		// 8192 == 45 degrees
		// TOLERANCES:
		// 1	61441 to 65535 
		//	 && 0 to 4095
		// 2	4096 to 12288
		// 3	12289 to 20480		
		// 4	20481 to 28672	            6 7 8
		// 5	28673 to 32768	             \|/
		//	 && -32678 to -28673           5 --+-- 1
		// 6	-20481 to -28672                  /|\
		// 7	-12289 to -20480                 4 3 2
		// 8	-4096 to -12288

What this means is that when the player draws with the mouse, numbers are generated and passed to the 'gestureID' array based on the angle between the mouse position and the previous mouse position. The little compass diagram shows which numbers are generated at which angle. The list of numbers next to it is the tolerance in degrees for each number (about 22.5 degrees each side of perfect, for a 45 degree angle tolerance. The large numbers are because Unreal calculates angles with a 65535 unit circle, instead of 360). At this stage, gesture testing seems to work pretty damn well. Curved shapes are even possible - a full clockwise circle starting at 12 o'clock gives the array [0,1,2,3,4,5,6,7,8,1]. That said, however, it may be tweaked later on.
Now, on to the code that does all this!

The timer() function in the inventory is now a little different, and relies on the calculation state for some of its elements.

 simulated function timer()
{    
     .......
        if(
cClick)
           {
            
grabinPRI(PC.PlayerReplicationInfo).playAmbient(Ptrue);
            
//play sndFX
            
drawing();
            if (!
cDraw)
            {
                
grabinPRI(PC.PlayerReplicationInfo).drawCursor(gEffect
                
spawnToplayerCamLoc); 
                  
//only draw cursor when not actually drawing shapes
                
gotoState(''); //this stops calculation
            
}
        } else {
            
grabinPRI(PC.PlayerReplicationInfo).playAmbient(Pfalse);
            
gotoState(''); //this stops calculation
            
            
if (gestureID.Length != 0)
            {  
//send gestureID, then purge out the old gesture buffer
                
                
gestureID.Remove(0gestureID.Length);
                
gestureID.Length 0;
            }
        }
           if (
cDraw)
          {
               if (!
isinState('gesturing'))
                   
gotoState('gesturing');
                   
//handle gesture calculation and effect
           
}
     .......                   



As you can see, it changes the inventory actor in and out of its 'gesturing' state (gotoState(''); returns it to the default empty state). It also clears out the 'gestureID' array when the right mouse button is released so that it is ready for a new gesture next time. Note the calls to the PRI for playAmbient() and playRelease() (not shown).

Now for the gesturing state itself.

 state gesturing
{
       
simulated function BeginState()
       {
        
firstTime true;
        
//get tolerance of mouse movement
        
tolerance calcTolerance();
       }

       
simulated function Tick(float DeltaTime)
       {
             
//vector calculation variables
        
local vector newVecdiffVec;
        
local rotator angleBetween;
        
local float diffSize;
         
         
grabinPRI(PC.PlayerReplicationInfo).drawEffect(hEffectspawnToplayerCamLoc);
                   
//draw effect onscreen
           
           
if (firstTime)
           {
                 
//return values to defaults
               
prevVec.PC.Player.WindowsMouseX;
               
prevVec.PC.Player.WindowsMouseY;
               
prevVec.0;
               
diffVec vect(0,0,0);
               
                 
//add a blank to the end of the array for pen down
               
gestureID.insert(gestureID.Length1);
            
gestureID[gestureID.Length 1] = 0
            
               
firstTime false;
           }
           
newVec.PC.Player.WindowsMouseX;
           
newVec.PC.Player.WindowsMouseY;
           
newVec.0;
               
//set up the vector holding mouse location
           
diffSize VSize(newVec prevVec);           
           if (
diffSize tolerance)
        {
             
//if distance is above tolerance change diff vector and reset prevVec
               
diffVec newVec prevVec;
            
prevVec newVec;
          
//find angle between 2 vectors and compute
               
angleBetween rotator(normal(diffVec));
               
computeOutput(angleBetween);
           }
    }

         
simulated function EndState()
         {
         }
    
    function 
computeOutput(rotator angle)
    {        
        
local int newDir//this is the new integer to add to array
        
local int Y//angle's Y val, makes things easier
        
        
angle.Yaw;
        
        if ( 
12288 >= && >= 4096 )
            
newDir 2;
        else if ( 
20480 >= && >= 12289 )
            
newDir 3;
        else if ( 
28672 >= && >= 20481 )
            
newDir 4;
        else if ( (
32768 >= && >= 28673) || (-32768 <= && <= -28673) )
            
newDir 5;
        else if ( -
28672 <= && <= -20481 )
            
newDir 6;
        else if ( -
20480 <= && <= -12289 )
            
newDir 7;
        else if ( -
12288 <= && <= -4096 )
            
newDir 8;
        else
            
newDir 1;  //find out which direction we draw in
        
        
if ( gestureID[gestureID.Length-1] != newDir )
        {  
//if direction has changed
            
gestureID.insert(gestureID.Length1);    //add an element
            
gestureID[gestureID.Length 1] = newDir//fill the new element
        
}
    }
    
    function 
float calcTolerance()
    {
        
local string CurrentRes;
            
//the screen resolution check for per-pixel tolerance calc
        
CurrentRes PC.ConsoleCommand("GETCURRENTRES");
        switch (
CurrentRes)
        {
            case 
"320x240":
                
tolerance 24;
                break;
            case 
"512x384":
                
tolerance 38;
                break;
            case 
"640x480":
                
tolerance 48;
                break;
            
//larger resolutions can be the same tolerance
            
default:
                
tolerance 50;
                break;
        }
        return 
tolerance;
    }


So, To examine the state flow. When it begins, it sets a global boolean, 'firstTime', to true. This simply tells us that the state has just been entered, that the previous mouse location must be set to the current one, and that all the other calculating variables must be reset. It also calls the state function calcTolerance(). This function is important for working out the allowance in mouse movement before a new integer is computed and passed to the 'gestureID' array. Since the mouse pos is calculated in pixels, lower resolutions need a smaller tolerance so they can draw the same complexity shapes. I decided that any resolution above 640x480 could use a 50 pixel tolerance range, but i may have to change that after more beta testing.
After beginState(), the program jumps to its Tick(). If I haven't explained before, Tick is a slightly different function than timer in Unreal, as it is executed every processor tick in the game rather than at set times. I think. Anyway, Tick() first draws the effect, then sets all its values back to default as explained before if it is the beginning of a new state execution (remember, after each shape is drawn, the state is exited and re-entered). Then it works out the current mouse position and checks if the difference between that position and the last mouse position is greater than the tolerance calculated from calcTolerance(). If it isn't, the player hasn't moved the mouse far enough yet so nothing further need happen. If it is, it calculates the angle between the two points and updates the previous mouse position to the current one. It then calls computeOutput().
This function simply pushes an integer onto the 'gestureID' array based on the angle between the 2 points. You may wonder at the negative values here, well, it was something that caused me some problems. Turns out that after 180 degrees, Unreal starts giving the rotation between the 2 points back in the opposite direction, you know, in order to get the smallest rotation between them. Took me a while to work that out.. I should talk about a useful debugging tip for interactively updated values.
It's the trusty HUD. Yep, that classic class thats oh-so very handy for showing you everything you need to know right in front of your face. HUDs have a function in them called drawHud(), which takes a canvas, 'C', as a parameter. What you can do is make your own custom HUD that extends HudBDeathMatch, and overload the drawHud() method to give you some useful debugging info. It's really easy to do, too. Just copy my readymade code.

     local color tempColor;
    
local byte tempstyle;
    
    
tempColor c.DrawColor;
    
tempstyle c.Style;

    
c.DrawColor=WhiteColor;
    
c.Style=ERenderStyle.STY_Additive;

    
DrawDebug(c);

    
c.DrawColor tempColor;//restore values
    
c.Style tempstyle


You may want to include a call to the superclasses method at the bottom if you still need the rest of the HUD. You can do that by simply adding Super.drawHud(C). All you need in your drawDebug() method are the lines

 c.SetPos(Xpos,Ypos);
c.DrawText(YOUR VARIABLE TO TESTtrue); 


Where Xpos and Ypos are X and Y locations in pixels from the top left of the screen and your variable to test is any variable which will be automatically casted to a string. I know this seems like a really stupidly simple thing, but its good to draw attention to it since it can be so damn useful. Oh, I should add that HUDs have handy 'pawnOwner' and 'playerOwner' vars which return the pawn and PC which own them, respectively.
- Also, I moved the gesture effect back to the player's face again. I figure slower movement speed will make its visibility issues while running forward much better.

14 April '04
- Tried to put if(!playerController(Controller).bBehindView) return false; at the start of grabinPawn's specialCalcView so that it only does it in behindview. Also had to add bSpecialCalcView=True to defaults. It didn't work. AND THEN...
- Gesturing works the whole way! I've got a gesture generating rockets.. at the map center cos I haven't done any aiming code yet, but hey! Success! I think it might need refining in the computation algorithm though, cos it seems a bit sensitive about the whole thing.
- SUCCESS!! delicious, wonderful success. Haven't bothered tweaking the recognition yet, but I know how to do it so it's ok. I have made a gesture (backwards C from bottom up) which fires a standard UT rocket. Simple and testiful. Basically, I copied a heap of variables and the doFireEffect() (with supporting classes) over from projectileFire and modded it all to suit my needs. It's very handy cos all I have to do to make new projectile - type attacks is reset the vars each time and change which class the projectile should be. It takes care of the rest. I can see myself copying over the ones for instantFires and all that, as well as adding some original functions to change other non-weapon things. It will all be waay too easy. I also looked into the possibility of having configurable strings for each gesture type, so that people can set the game up differently if they want to. It shouldn't be too hard, but I think i won't do it because people would be dicks and set everything to a straight line.
Interpreting the array from gestureInventory proved to be seriously easy:

 simulated function interp(array<intgestureID)
{
    
local int i;
    
local string strID;
        
//this is kind of dumb, but better accessible. It stores the ID array
        // as an easy, non-associative datatype. Stops us needing many iterators.
    
    
for ( i=0gestureID.Lengthi++ )
    {  
//converts to string
        
strID strID gestureID[i];
    }
    
    
debugStr strID;    //FOR DEBUGGING
    
    
setDefProj();
    if ( 
strID == fireballStr )
        
fireBall();


That's it! All the hard work is already done in the inventory. setDefProj() just resets all the projectile's settings (spread, number, damage mult and offset) so we don't have to do it each time. Then we just have REALLY simple functions for each attack. For my test attack:

 function fireBall()
{
    
ProjectileClass = Class'XWeapons.RocketProj';
    
ProjSpawnOffset.50.000;
    
DoProjectile();


Oh, and the 'fireballStr' from the interp function is nice and easy, and it sits down at the bottom in defaultproperties. Too. Friggin. Easy. And that's all for today!

15 April '04
- Set up a temporary HUD and also added in 'held gesture' functionality. Basically, some attacks need to be 'held' for a while before the player uses them. If you had an instant-effect rocket, for example, it would be rather difficult to avoid blowing yourself up, particularly when you can't rotate your view while in gesture mode. The HUD now brings up a crosshair when a held gesture happens, and also a little countdown timer which tells you how much time you have left before your gesture expires, and hence before you have to redraw it. Left-clicking fires a held gesture, and right-clicking automatically cancels it before attempting to draw another. To do this, I made a new state in the PRI:

 state holdProjAttack
{  //hold the projectile attack until the user clicks again or 5 seconds
    
simulated function beginState()
    {
        
countDown holdTime;
        
setTimer(1.0,true);
    }
    
    
simulated function timer()
    {
        
countDown--;
        if (
countDown <= 0)
            
gotoState('');
    }
    
    
simulated function endState()
    {
        if ( 
grabinPawn(Controller(Owner).Pawn).GestureInv.cRelease )
            
DoProjectile();
        
grabinPawn(Controller(Owner).Pawn).GestureInv.cRelease false;
        
countDown holdTime+1;
    }


This makes things easier for future expansion, as any projectile gesture can just call this state for it to work. Of course, I'll need to add more similar states for other gestures that need to be held, but most likely there won't be many more of those. 'countDown' is sent to the HUD class along with 'holdTime' to work out the timer. The HUD checks if countDown is greater than holdTime before rendering:

 if ( grabinPRI(PlayerController(PawnOwner.Controller).PlayerReplicationInfo)
    .
isinState('holdProjAttack') )
    {
    
c.SetPosc.ClipX/2c.ClipY/);
    
c.DrawIcon(xHair,0.5);    
    if (
grabinPRI(PlayerController(PawnOwner.Controller).PlayerReplicationInfo).countDown
     
<= grabinPRI(PlayerController(PawnOwner.Controller).PlayerReplicationInfo).holdTime)
    {
        
c.SetPosc.ClipX/32c.ClipY/32 );
        
c.DrawText(grabinPRI(PlayerController(PawnOwner.Controller)
                .
PlayerReplicationInfo).countDowntrue);
    }
    } 


I of course needed to add a little to the interaction to work in this stuff, but it wasn't that complicated so I wont go into it here. And that is essentially all there is to it.
- Fixed up the pawn's view calculation code and it works perfectly. Just a few little errors there, nothing new.
- I implemented another tolerance in the gesture recognition algorithm which has eliminated the 'round corners on straight edged shapes' bug. Now, after a certain number of game ticks (i found through testing that 8 was a good number) the prevVec variable is set to the mouse position. This means that it is then calculated properly from the corner of the shape. Gesturing seems to work perfectly, and looks pretty finalised (:
- Had to move the 'holdProjAttack' state over to the inventory class to get it to replicate properly. There were a lot of horrible replication issues getting the PRI (which exists serverside) to communicate with the inventory (which exists clientside, i think). But i got there without too much fuss. Grabin once again works on LAN play.
- Augmented the HUD so that the crosshair looks like a little hand, and fades gently away when you fire a gesture out.
- Changed the test projectile into the gasbag firemode so it looks more like a fireball (:
- Added some random projectile classes to test with (gasbag fireball, krall bolts, Juggernaught rocket and instant-hit sentinel laser) and started some serious LAN action! First alpha test screenshots coming soon!!


Well, I think that about concludes the open-source part of the project. We have a working gesture system which recognises player input without a hitch and the ability to easily expand it to create different effects and attacks extremely easily. We've learnt a lot about interactions, replication, vector maths and weapon code - elements of uScript which are understood to be particularly difficult. But now it is time to close down the see-all aspects of this journal, for the element of surprise is still important in a mod release. More importantly though, i am getting tired of long-winded write-ups when i could be doing more code.
As to what needs to be finalised/done, the mod's netcode needs some work- in particular it needs to cache some gesture effect positions before sending them to stop possible lag in that regard. The sword needs animating, and a weapon model needs to be created for the 'gesture gun' dummy so gesturing looks cooler, and weapons need to be removed from UT's weapon selection dropdown lists. Gestures with different outcomes than projectile weapons need to be implemented, probably really easily by new functions in grabinPRI. The code to send the gestureID array to other world objects needs to be created and an interface between those objects and the player decided upon. Player speed needs to be reduced to a more suitable level. The gesture effect needs to be made more noticeable so that other players can see what people are drawing and prepare a counter to it. Also, i cant say my code has stuck with the principles of OOP at all, in fact a lot of it is pretty messy and uses a lot of stupid accessor functions. But the important thing we should always remember is that it WORKS. After i fix these things, it is just a matter of designing additional gesture effects and the levels, sounds, music etc before a Grabin release comes about.
Dont feel sad about this cessation of open-sourcedness though, i will keep updating this briefly with additions and code changes i put into the mod. Also, if you write to me and ask for the code i'll be more than happy to give it to you if you have a good reason for wanting it.
Here's the latest build of the mod.


18 April '04
- Set up the player speed and motion properties properly so the game is slower and more suited to its nature. Also removed double jumping abilities.
- Removed the sword and gesture gun weapons from the UT weapon dropdown list. I was just missing some class keywords.
- Made the gesture effect a little more visible.
- Moved the gesture effect out in front of the player more and scaled it so that cursor position lines up with window position again. It is much more visible and effective in multiplayer games now.
- Found out about the rather useful 'bCanHitOwner' property for projectiles that will be good for spawning them closer to the player.
- Added a buffer to the gesture effect sending code so that slower connections won't die. Also, all the xEmitters spawned are now held in an array on the PRI, which is destroyed with the PRI. Each xEmitter reduces the length of this array when destroyed.
- Took all that buffer coding away when i realised that due to an unrealscript limitation, dynamic arrays are not replicated, and so it doesnt exactly work.

25 April '04
- Well, not a lot has been happening here lately due to some kind of huge uni workload that really, really hurts my free time. But, on the other hand, I have the following news:
- Fully optimised the gesture drawing effect so that is insanely smooth, low bandwith, and has a new look.
- Some ideas for a singleplayer plot are in my head now, i'll make a writeup soon. I will probably release a multiplayer version of the mod first, then develop the singleplayer aspect of the game. It's gonna take a long time, and i want something released sooner so that people can enjoy it.. erm, quicker.
- Got rid of the cursor effect emitter and replaced it with a HUD-type drawing crosshair. I won't talk much about these for now, but some screenies will go up soon.
- Started work on the gravity redirection gesture and ran into some problems, but i'll look into it more later on when i am satisfied with the new gesture look.
- Ok, im pretty satisfied. Here's a screenshot which i guess probably gives some indication of where I'm heading with the mod right now.

3/04 | 4/04 | <-Open Closed-> Last Free Code | 5/04 | 6/04 | Most Recent