Beginning Your Game Part 4   15 comments

Video Version

Subject: Beginning Your Game Part 4
Skill Level: Beginner
Run-Time: 54 Minutes
Author: Michael Allar
Notes: Setting up HTInventoryManager, HTInventory, HTWeapon

Streaming:     720×480 1920×1080

Download:     Low-Res (32MB) Hi-Res (200MB)

Written Version

Subject: Beginning Your Game Part 4
Skill Level: Beginner
Author: Michael Allar
Notes: Setting up HTInventoryManager, HTInventory, HTWeapon

See the video for an in-depth explanation.

HTInventoryManager

/*******************************************************************************

 HTInventoryManager

 Creation date: 08/03/2010 07:03

 Lots of code stolen from Epic
*******************************************************************************/

class HTInventoryManager extends InventoryManager
 config(Game);

/** Holds the last weapon used */
var Weapon PreviousWeapon;

simulated function GetWeaponList(out array<HTWeapon> WeaponList, optional bool bNoEmpty)
{
 local HTWeapon Weap;

 ForEach InventoryActors( class'HTWeapon', Weap )
 {
 if ( !bNoEmpty || Weap.HasAnyAmmo())
 {
 WeaponList.Insert(0,1);
 WeaponList[0] = Weap;
 }
 }
}
/**
 * Accessor for the server to begin a weapon switch on the client.
 *
 * @param    DesiredWeapon        The Weapon to switch to
 */

reliable client function ClientSetCurrentWeapon(Weapon DesiredWeapon)
{
 SetPendingWeapon(DesiredWeapon);
}

simulated function Inventory CreateInventory(class<Inventory> NewInventoryItemClass, optional bool bDoNotActivate)
{
 if (Role==ROLE_Authority)
 {
 return Super.CreateInventory(NewInventoryItemClass, bDoNotActivate);
 }
 return none;
}
/**
 * Handle AutoSwitching to a weapon
 */
simulated function bool AddInventory( Inventory NewItem, optional bool bDoNotActivate )
{
 local bool bResult;

 if (Role == ROLE_Authority)
 {
 bResult = super.AddInventory(NewItem, bDoNotActivate);
 }
 return bResult;
}

simulated function DiscardInventory()
{
 local Vehicle V;

 if (Role == ROLE_Authority)
 {
 Super.DiscardInventory();

 V = Vehicle(Owner);
 if (V != None && V.Driver != None && V.Driver.InvManager != None)
 {
 V.Driver.InvManager.DiscardInventory();
 }
 }
}

simulated function RemoveFromInventory(Inventory ItemToRemove)
{
 if (Role==ROLE_Authority)
 {
 Super.RemoveFromInventory(ItemToRemove);
 }
}
/**
 * Scans the inventory looking for any of type InvClass.  If it finds it it returns it, other
 * it returns none.
 */
function Inventory HasInventoryOfClass(class<Inventory> InvClass)
{
 local inventory inv;

 inv = InventoryChain;
 while(inv!=none)
 {
 if (Inv.Class==InvClass)
 return Inv;

 Inv = Inv.Inventory;
 }
 return none;
}

/**
 * Store the last used weapon for later
 */
simulated function ChangedWeapon()
{
 PreviousWeapon = Instigator.Weapon;
 Super.ChangedWeapon();
}

simulated function SwitchToPreviousWeapon()
{
 if ( PreviousWeapon!=none && PreviousWeapon != Pawn(Owner).Weapon )
 {
 PreviousWeapon.ClientWeaponSet(false);
 }
}

defaultproperties
{
 bMustHoldWeapon=true
 PendingFire(0)=0
 PendingFire(1)=0
}

HTInventory

/*******************************************************************************
 HTInventory

 Creation date: 08/03/2010 07:06
 Copyright (c) 2010, Allar

*******************************************************************************/

class HTInventory extends Inventory;

HTWeapon

/*******************************************************************************
 HTWeapon

 Creation date: 08/03/2010 06:21
 Copyright (c) 2010, Allar

*******************************************************************************/

class HTWeapon extends UDKWeapon;

/** Current ammo count */
var repnotify int AmmoCount;

/** Max ammo count */
var int MaxAmmoCount;

/** Holds the amount of ammo used for a given shot */
var array<int> ShotCost;

/** Offset from view center */
var(FirstPerson) vector    PlayerViewOffset;

replication
{
 // Server->Client properties
 if ( bNetOwner )
 AmmoCount;
}

simulated event ReplicatedEvent(name VarName)
{
 if ( VarName == 'AmmoCount' )
 {
 if ( !HasAnyAmmo() )
 {
 WeaponEmpty();
 }
 }
 else
 {
 Super.ReplicatedEvent(VarName);
 }
}

simulated function int GetAmmoCount()
{
 return AmmoCount;
}
 /*
 * Consumes some of the ammo
 */
function ConsumeAmmo( byte FireModeNum )
{
 // Subtract the Ammo
 AddAmmo(-ShotCost[FireModeNum]);
}

/**
 * This function is used to add ammo back to a weapon.  It's called from the Inventory Manager
 */
function int AddAmmo( int Amount )
{
 AmmoCount = Clamp(AmmoCount + Amount,0,MaxAmmoCount);
 return AmmoCount;
}

/**
 * Returns true if the ammo is maxed out
 */
simulated function bool AmmoMaxed(int mode)
{
 return (AmmoCount >= MaxAmmoCount);
}

/**
 * This function checks to see if the weapon has any ammo available for a given fire mode.
 *
 * @param    FireModeNum        - The Fire Mode to Test For
 * @param    Amount            - [Optional] Check to see if this amount is available.  If 0 it will default to checking
 *                              for the ShotCost
 */
simulated function bool HasAmmo( byte FireModeNum, optional int Amount )
{
 if (Amount==0)
 return (AmmoCount >= ShotCost[FireModeNum]);
 else
 return ( AmmoCount >= Amount );
}

/**
 * returns true if this weapon has any ammo
 */
simulated function bool HasAnyAmmo()
{
 return ( ( AmmoCount > 0 ) || (ShotCost[0]==0 && ShotCost[1]==0) );
}

/**
 * This function retuns how much of the clip is empty.
 */
simulated function float DesireAmmo(bool bDetour)
{
 return (1.f - float(AmmoCount)/MaxAmmoCount);
}

/**
 * Returns true if the current ammo count is less than the default ammo count
 */
simulated function bool NeedAmmo()
{
 return ( AmmoCount < Default.AmmoCount );
}

/**
 * Cheat Help function the loads out the weapon
 *
 * @param     bUseWeaponMax     - [Optional] If true, this function will load out the weapon
 *                              with the actual maximum, not 999
 */
simulated function Loaded(optional bool bUseWeaponMax)
{
 if (bUseWeaponMax)
 AmmoCount = MaxAmmoCount;
 else
 AmmoCount = 999;
}

/**
 * Called when the weapon runs out of ammo during firing
 */
simulated function WeaponEmpty()
{
 // If we were firing, stop
 if ( IsFiring() )
 {
 GotoState('Active');
 }

 if ( Instigator != none && Instigator.IsLocallyControlled() )
 {
 Instigator.InvManager.SwitchToBestWeapon( true );
 }
}

/*********************************************************************************************
 * Ammunition / Inventory
 *********************************************************************************************/

function PrintScreenDebug(string debugText)
{
 local PlayerController PC;
 PC = PlayerController(Pawn(Owner).Controller);
 if (PC != None)
 PC.ClientMessage("HTWeapon: " $ debugText);
}

simulated function AttachWeaponTo( SkeletalMeshComponent MeshCpnt, optional Name SocketName )
{
 local HTPawn HTP;

 HTP = HTPawn(Instigator);
 PrintScreenDebug("Attaching Weapon");
 // Attach 1st Person Muzzle Flashes, etc,
 if ( Instigator.IsFirstPerson() )
 {
 AttachComponent(Mesh);
 EnsureWeaponOverlayComponentLast();
 SetHidden(False);
 Mesh.SetLightEnvironment(HTP.LightEnvironment);
 PrintScreenDebug("First Person Weapon Attached");
 }
 else
 {
 SetHidden(True);
 if (HTP != None)
 {
 Mesh.SetLightEnvironment(HTP.LightEnvironment);
 }
 }
 //SetSkin(HTPawn(Instigator).ReplicatedBodyMaterial);
}

simulated event SetPosition(UDKPawn Holder)
{
 local vector DrawOffset, ViewOffset, FinalLocation;
 local rotator NewRotation, FinalRotation, SpecRotation;
 local PlayerController PC;
 local vector2D ViewportSize;
 local bool bIsWideScreen;
 local vector SpecViewLoc;

 if ( !Holder.IsFirstPerson() )
 return;

 Mesh.SetHidden(False);

 foreach LocalPlayerControllers(class'PlayerController', PC)
 {
 LocalPlayer(PC.Player).ViewportClient.GetViewportSize(ViewportSize);
 break;
 }
 bIsWideScreen = (ViewportSize.Y > 0.f) && (ViewportSize.X/ViewportSize.Y > 1.7);

 Mesh.SetScale3D(default.Mesh.Scale3D);
 Mesh.SetRotation(default.Mesh.Rotation);

 ViewOffset = PlayerViewOffset;

 // Calculate the draw offset
 if ( Holder.Controller == None )
 {

 if ( DemoRecSpectator(PC) != None )
 {
 PC.GetPlayerViewPoint(SpecViewLoc, SpecRotation);
 DrawOffset = ViewOffset >> SpecRotation;
 //DrawOffset += UTPawn(Holder).WeaponBob(BobDamping, JumpDamping);
 FinalLocation = SpecViewLoc + DrawOffset;
 SetLocation(FinalLocation);
 SetBase(Holder);

 // Add some rotation leading
 //SpecRotation.Yaw = LagRot(SpecRotation.Yaw & 65535, LastRotation.Yaw & 65535, MaxYawLag, 0);
 //SpecRotation.Pitch = LagRot(SpecRotation.Pitch & 65535, LastRotation.Pitch & 65535, MaxPitchLag, 1);
 //LastRotUpdate = WorldInfo.TimeSeconds;
 //LastRotation = SpecRotation;

 if ( bIsWideScreen )
 {
 //SpecRotation += WidescreenRotationOffset;
 }
 SetRotation(SpecRotation);
 return;
 }
 else
 {
 DrawOffset = (ViewOffset >> Holder.GetBaseAimRotation()) + HTPawn(Holder).GetEyeHeight() * vect(0,0,1);
 PrintScreenDebug("Setting DrawOffset to Holder Info");
 }
 }
 else
 {

 DrawOffset.Z = HTPawn(Holder).GetEyeHeight();
 //DrawOffset += HTPawn(Holder).WeaponBob(BobDamping, JumpDamping);

 if ( HTPlayerController(Holder.Controller) != None )
 {
 DrawOffset += HTPlayerController(Holder.Controller).ShakeOffset >> Holder.Controller.Rotation;
 }

 DrawOffset = DrawOffset + ( ViewOffset >> Holder.Controller.Rotation );
 }

 // Adjust it in the world
 FinalLocation = Holder.Location + DrawOffset;
 SetLocation(FinalLocation);
 SetBase(Holder);

 NewRotation = (Holder.Controller == None) ? Holder.GetBaseAimRotation() : Holder.Controller.Rotation;

 // Add some rotation leading
 //if (Holder.Controller != None)
 //{
 //    FinalRotation.Yaw = LagRot(NewRotation.Yaw & 65535, LastRotation.Yaw & 65535, MaxYawLag, 0);
 //    FinalRotation.Pitch = LagRot(NewRotation.Pitch & 65535, LastRotation.Pitch & 65535, MaxPitchLag, 1);
 //    FinalRotation.Roll = NewRotation.Roll;
 //}
 //else
 //{
 FinalRotation = NewRotation;
 //}
 //LastRotUpdate = WorldInfo.TimeSeconds;
 //LastRotation = NewRotation;

 if ( bIsWideScreen )
 {
 //FinalRotation += WidescreenRotationOffset;
 }
 SetRotation(FinalRotation);
}

simulated state WeaponEquipping
{
 simulated event BeginState(Name PreviousStateName)
 {
 PrintScreenDebug("Weapon Equipping");
 AttachWeaponTo(Instigator.Mesh);
 Super.BeginState(PreviousStateName);
 }
}

simulated state Active
{
 simulated event BeginState(Name PreviousStateName)
 {
 PrintScreenDebug("Active");
 Super.BeginState(PreviousStateName);
 }
}

simulated state WeaponFiring
{
 simulated event BeginState(Name PreviousStateName)
 {
 PrintScreenDebug("Firing");
 Super.BeginState(PreviousStateName);
 }

 /**
 * We override BeginFire() so that we can check for zooming and/or empty weapons
 */

 simulated function BeginFire( Byte FireModeNum )
 {
 // No Ammo, then do a quick exit.
 if( !HasAmmo(FireModeNum) )
 {
 WeaponEmpty();
 return;
 }
 Global.BeginFire(FireModeNum);
 }
}

defaultproperties
{
 Begin Object Class=UDKSkeletalMeshComponent Name=FirstPersonMesh
 DepthPriorityGroup=SDPG_Foreground
 bOnlyOwnerSee=true
 bOverrideAttachmentOwnerVisibility=true
 CastShadow=false
 bAllowAmbientOcclusion=false
 End Object
 Mesh=FirstPersonMesh

 Begin Object Class=SkeletalMeshComponent Name=PickupMesh
 bOnlyOwnerSee=false
 CastShadow=false
 bForceDirectLightMap=true
 bCastDynamicShadow=false
 CollideActors=false
 BlockRigidBody=false
 bUseAsOccluder=false
 MaxDrawDistance=6000
 bForceRefPose=1
 bUpdateSkelWhenNotRendered=false
 bIgnoreControllersWhenNotRendered=true
 bAcceptsStaticDecals=FALSE
 bAcceptsDynamicDecals=FALSE
 bAllowAmbientOcclusion=false
 End Object
 DroppedPickupMesh=PickupMesh
 PickupFactoryMesh=PickupMesh

 MessageClass=class'UTPickupMessage'
 DroppedPickupClass=class'UTDroppedPickup'

 FiringStatesArray(0)=WeaponFiring
 FiringStatesArray(1)=WeaponFiring

 WeaponFireTypes(0)=EWFT_InstantHit
 WeaponFireTypes(1)=EWFT_InstantHit

 WeaponProjectiles(0)=none
 WeaponProjectiles(1)=none

 FireInterval(0)=+0.3
 FireInterval(1)=+0.3

 Spread(0)=0.0
 Spread(1)=0.0

 ShotCost(0)=1
 ShotCost(1)=1

 AmmoCount=5
 MaxAmmoCount=5

 InstantHitDamage(0)=0.0
 InstantHitDamage(1)=0.0
 InstantHitMomentum(0)=0.0
 InstantHitMomentum(1)=0.0
 InstantHitDamageTypes(0)=class'DamageType'
 InstantHitDamageTypes(1)=class'DamageType'
 WeaponRange=22000

 ShouldFireOnRelease(0)=0
 ShouldFireOnRelease(1)=0

 DefaultAnimSpeed=0.9

 EquipTime=+0.45
 PutDownTime=+0.33
}

Posted March 9, 2010 by Allar in Unreal

Tagged with , , , , , , , ,

15 responses to Beginning Your Game Part 4

Subscribe to comments with RSS.

  1. Thanks for your great tutes mate.

  2. I am getting an error in the weapon class

    Error, BEGIN OBJECT: No base template named FirstPersonMesh found in parent class UDKWeapon: Begin Object Name=FirstPersonMesh

    • Code updated for the defaultproperties block of HTWeapon.

      Apparently when copy+pasting from WOTgreal, the "class" part gets ripped out for whatever reason.

  3. Could U plz put the class HTPlayerController…

    because I downloal the low resoluion..

  4. (Using April UDK)
    I'm getting this warning:

    Warning, Variable declaration: 'AmmoCount' conflicts with previously defined field in 'UDKWeapon'

    When compiling your code. I know that AmmoCount already exists in UDKWeapon, and commenting out the line gives me a replication error.

    Would it be safe to rename all AmmoCount instances in the code to something like CurAmmoCount or something, without damaging the system?

    I know it's a warning and can be ingored, but yellow text scares me :3

  5. (((((( (Using April UDK)
    I’m getting this warning:

    Warning, Variable declaration: ‘AmmoCount’ conflicts with previously defined field in ‘UDKWeapon’

    When compiling your code. I know that AmmoCount already exists in UDKWeapon, and commenting out the line gives me a replication error.

    Would it be safe to rename all AmmoCount instances in the code to something like CurAmmoCount or something, without damaging the system?

    I know it’s a warning and can be ingored, but yellow text scares me :3)))))))

    Yes by switching AmmoCount to CurAmmoCount will get rid of the warnings you get. Switch it to this —var repnotify int CurAmmoCount;— and the same for any where you see AmmoCount I repeat any where!! LOL

  6. class HTWeapon extends UDKWeapon;

    /** Current ammo count, Switched name to CurAmmoCount to avoid error
    * ('AmmoCount' conflicts with previously defined field in 'UDKWeapon')
    */
    var repnotify int CurAmmoCount;

    /** Max ammo count */
    var int MaxAmmoCount;

    /** Holds the amount of ammo used for a given shot */
    var array ShotCost;

    /** Offset from view center */
    var(FirstPerson) vector PlayerViewOffset;

    replication
    {
    // Server->Client properties
    if ( bNetOwner )
    CurAmmoCount;
    }

    simulated event ReplicatedEvent(name VarName)
    {
    if ( VarName == 'CurAmmoCount' )
    {
    if ( !HasAnyAmmo() )
    {
    WeaponEmpty();
    }
    }
    else
    {
    Super.ReplicatedEvent(VarName);
    }
    }

    simulated function int GetAmmoCount()
    {
    return CurAmmoCount;
    }
    /*
    * Consumes some of the ammo
    */
    function ConsumeAmmo( byte FireModeNum )
    {
    // Subtract the Ammo
    AddAmmo(-ShotCost[FireModeNum]);
    }

    /**
    * This function is used to add ammo back to a weapon. It's called from the Inventory Manager
    */
    function int AddAmmo( int Amount )
    {
    AmmoCount = Clamp(AmmoCount + Amount,0,MaxAmmoCount);
    return CurAmmoCount;
    }

    /**
    * Returns true if the ammo is maxed out
    */
    simulated function bool AmmoMaxed(int mode)
    {
    return (CurAmmoCount >= MaxAmmoCount);
    }

    /**
    * This function checks to see if the weapon has any ammo available for a given fire mode.
    *
    * @param FireModeNum – The Fire Mode to Test For
    * @param Amount – [Optional] Check to see if this amount is available. If 0 it will default to checking
    * for the ShotCost
    */
    simulated function bool HasAmmo( byte FireModeNum, optional int Amount )
    {
    if (Amount==0)
    return (CurAmmoCount >= ShotCost[FireModeNum]);
    else
    return ( CurAmmoCount >= Amount );
    }

    /**
    * returns true if this weapon has any ammo
    */
    simulated function bool HasAnyAmmo()
    {
    return ( ( CurAmmoCount > 0 ) || (ShotCost[0]==0 && ShotCost[1]==0) );
    }

    /**
    * This function retuns how much of the clip is empty.
    */
    simulated function float DesireAmmo(bool bDetour)
    {
    return (1.f – float(CurAmmoCount)/MaxAmmoCount);
    }

    /**
    * Returns true if the current ammo count is less than the default ammo count
    */
    simulated function bool NeedAmmo()
    {
    return ( CurAmmoCount < Default.CurAmmoCount );
    }

    /**
    * Cheat Help function the loads out the weapon
    *
    * @param bUseWeaponMax – [Optional] If true, this function will load out the weapon
    * with the actual maximum, not 999
    */
    simulated function Loaded(optional bool bUseWeaponMax)
    {
    if (bUseWeaponMax)
    CurAmmoCount = MaxAmmoCount;
    else
    CurAmmoCount = 999;
    }

    /**
    * Called when the weapon runs out of ammo during firing
    */
    simulated function WeaponEmpty()
    {
    // If we were firing, stop
    if ( IsFiring() )
    {
    GotoState('Active');
    }

    if ( Instigator != none && Instigator.IsLocallyControlled() )
    {
    Instigator.InvManager.SwitchToBestWeapon( true );
    }
    }

  7. When I launch the game it has no hud or physics. I can fly around. Is that suppose to happen?

  8. O.k. , good tutorial but consider some things:

    1) When you were taking out functions you talked as if the average joe knew what the hell all that stuff did, and you should consider that some people are relatively new to programming and udk and don't know all of Epic's logic functions.

    2) You kept referencing (verbally) to things that the user hasnt made yet and thats pretty confusing…like when you were explaining the inventory/weapon classes prior to us creating them.

    3) The video tutorial was pretty dense and felt like it had alot of filler and unnecessary comments (again read number 1) about classes we haven't made.

    4) Your tutorials explain how udk organizes it's classes and the general format for manipulating their organization structure to fit your own game but you don't explain unreal script itself. (I code in C++ so I recognize all the logic statements if-else switch etc.) but you don't explain variables, unique statements, or anything else.

    5) You stated we we're building from the ground up, but you just randomly copied a bunch of stuff from epic and deleted other stuff. It would be easier to actually learn what everything does and how to set it up if you started from scratch. I don't like to copy paste I actually want to know what it does so I don't have to revisit old code 300 times every-time I want to do something.

    This is not a rant, it is just a list of problems and suggestions to fix it. If it sounds as if I'm being cruel, i'm sorry, but as you know its hard to show emotion of the internet.

    • This tutorial is fine. He basically explains if most of the logic is already there, why rewrite it? It is our jobs to do the research and see what functions talk to one another (not that it is that hard with "search" :P ).

      There are plenty of websites that explain in great depth on the different types of functions and properties that are in the UDK base classes.

      Your GameInfo class creates the inventory manager for the player when they join and assigns the player's Pawn as it's owner.

      The inventory manager creates a "chain" of the inventory class (aka UTWeapon, HTWeapon, EtcWeapon). Since your different weapons extend your base weapon class, it still counts as your base weapon class AND an inventory object. When you get a PowerUp, it is put into your inventory, but since it is not a weapon, it does not come up in your weapon list (the weapon list function) because that is how it was coded.

      There are webs of code integrated inside the UDK. Start from some function and see what talks to it and what it talks to; pick it a part. You'll learn a lot. Good luck! :)

  9. ¿what is a "simulated function"? I know functions already. Thanks. (btw, I suscribe to what SoulTrappingDreidal said)

  10. I am getting this error and compiling fails….
    C:UDKUDK-2011-04Binaries..DevelopmentSrcUDKGameClassesUDKGame.uc : Warning, Duplicate class name: UDKGame also exists in package UDKBase

    • Basically, Epic added their own class named UDKGame to the newer UDK Betas. So, you'll have to rename Allar's UDKGame to something else.

      (Remember to update its class definition, too. Oh, and update TheHuntGame's class definition while you're at it.) :)

  11. If you want to keep "bFilter", the b- is for "bool" and the "Filter" is to activate the GroupFilter variable (third param).

    If true, it will rotate through a group of weapons instead of all of them. Example would be if you have the number 4 button set to rotate through only a set of rifles only compared to the number 3 button set to rotate through only pistols only.

    If false, it returns the entire list. Unless bNoEmpty is set to true, then it returns only the weapons that have ammo.

    I would suggest keeping it, IMHO. It can be very useful. ;)

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>