Top Down Shooter (Isometric Camera, WASD Controls, Aiming at Mouse Cursor)

Goal:

We want to create a new Game Type similar to “Alien Swarm“.
The camera view will be isometric.
The player character can be controlled with WASD according to the camera view.
The player has a moving cross hair which sets player’s viewing direction as well as the firing direction.

This tutorial is completely done in Unreal Script.

Special thanks go to:

Roychr – his isometric camera tutorial
X9 Productions – their isometric camera tutorial
Zaiteria – his isometric camera and controls tutorial
ZomBPir8Ninja – Scaleform Approach with startDrag()

 

Screenshot

Alien Swarm Tutorial

HEADS UP:

For more information on which tools to use, how to set up a new game, etc. please check out the UDK Forums as well as the UDN. The folks over there are really helpful and know a thing or two.

This is an advanced tutorial and I will assume that there is a basic understanding of Unreal Script, Pawns, Game Info, Player Controller, setting up the folder structure, etc.
However you should be able to copy-paste the code into your script files and everything should be fine.

I use DM-Deck as default map.

Issues:

  • Currently the Weapon Mesh is not displayed correctly.
  • Feign Death breaks the system.

Game info

There is not so much going on here but assigning the default Pawn class, the Player Controller and HUD.
The player also gets a Link Gun. Yeah!

TestGameInfo.uc

  1. class TestGameInfo extends GameInfo;
  2. //Create inventory
  3. var array< class <Inventory> > DefaultInventory;
  4. //Add default items to inventory of PlayerPawn
  5. function AddDefaultInventory( pawn PlayerPawn )
  6. {
  7.     if(PlayerPawn.IsHumanControlled() )
  8.     {
  9.         PlayerPawn.CreateInventory(class’UTWeap_LinkGun’,false);
  10.     }
  11. }
  12. defaultproperties
  13. {
  14.         bDelayedStart=false
  15.         HUDType=class’MouseInterfaceHUD’
  16.         PlayerControllerClass=class’TestPlayerController’
  17.         DefaultPawnClass=class’TestPawn’
  18. }

Pawn

The Pawn has the camera functions built into it so we don’t need a separate camera class.

TestPawn.uc

  1. class TestPawn extends UTPawn
  2.         config(Game)
  3.         notplaceable;
  4. //Camera Variables
  5. var float CamOffsetDistance; //distance to offset the camera from the player
  6. var int IsoCamAngle; //pitch angle of the camera
  7. //Makes the player mesh visable
  8. simulated event BecomeViewTarget( PlayerController PC )
  9. {
  10.    local UTPlayerController UTPC;
  11.    Super.BecomeViewTarget(PC);
  12.    if (LocalPlayer(PC.Player) != None)
  13.    {
  14.       UTPC = TestPlayerController(PC);
  15.       if (UTPC != None)
  16.       {
  17.          //set player controller to behind view and make mesh visible
  18.          UTPC.SetBehindView(true);
  19.          SetMeshVisibility(true);
  20.          UTPC.bNoCrosshair = true;
  21.       }
  22.    }
  23. }
  24. // Uses the weapon height (if any), otherwise eye height, to define the start location of the projectile. Essentially, it causes the pawn to look straight ahead if you were in 1st person view.
  25. simulated function vector GetAimStartLocation()
  26. {
  27.         local Vector pawnAimLocation;
  28.         if (Weapon != none)
  29.         {
  30.                 pawnAimLocation = Location + vect(0,0,1) * (Weapon.GetPhysicalFireStartLoc().Z – Location.Z); // just use upvector
  31.                 return pawnAimLocation;
  32.         }
  33.         else
  34.         {
  35.                 return GetPawnViewLocation(); // eye fallback
  36.         }
  37. }
  38. //Gives us our camera view.
  39. simulated function bool CalcCamera( float fDeltaTime, out vector out_CamLoc, out rotator out_CamRot, out floatout_FOV )
  40. {
  41.    out_CamLoc = Location;
  42.    out_CamLoc.X -= Cos(IsoCamAngle * UnrRotToRad) * CamOffsetDistance;
  43.    out_CamLoc.Z += Sin(IsoCamAngle * UnrRotToRad) * CamOffsetDistance;
  44.    out_CamRot.Pitch = -1 * IsoCamAngle;
  45.    out_CamRot.Yaw = 0;
  46.    out_CamRot.Roll = 0;
  47.    return true;
  48. }
  49. //Returns Aim Rotation
  50. simulated singular event Rotator GetBaseAimRotation()
  51. {
  52.    local rotator POVRot, tempRot;
  53.    tempRot = Rotation;
  54.    tempRot.Pitch = 0;
  55.    SetRotation(tempRot);
  56.    POVRot = Rotation;
  57.    POVRot.Pitch = 0;
  58.    return POVRot;
  59. }
  60. defaultproperties
  61. {
  62.         Begin Object Class=SkeletalMeshComponent Name=InitialSkeletalMesh
  63.                 CastShadow=true
  64.                 bCastDynamicShadow=true
  65.                 bOwnerNoSee=false
  66.                 LightEnvironment=MyLightEnvironment;
  67.         BlockRigidBody=true;
  68.         CollideActors=true;
  69.         BlockZeroExtent=true;
  70.                 PhysicsAsset=PhysicsAsset’CH_AnimCorrupt.Mesh.SK_C H_Corrupt_Male_Physics’
  71.                 AnimSets(0)=AnimSet’CH_AnimHuman.Anims.K_AnimHuman _AimOffset’
  72.                 AnimSets(1)=AnimSet’CH_AnimHuman.Anims.K_AnimHuman _BaseMale’
  73.                 AnimTreeTemplate=AnimTree’CH_AnimHuman_Tree.AT_CH_ Human’
  74.                 SkeletalMesh=SkeletalMesh’CH_LIAM_Cathode.Mesh.SK_ CH_LIAM_Cathode’
  75.         End Object
  76.         Mesh=InitialSkeletalMesh;
  77.         Components.Add(InitialSkeletalMesh);
  78.    IsoCamAngle=8000 //6420 = 35.264 degrees Change this number for the camera angle
  79.    CamOffsetDistance=512 //change this number for camera distance.
  80.    GroundSpeed=256
  81. }

Player Controller

The Player Controller handles all the functions regarding mouse input, the pawn facing the mouse and the WASD movement.

TestPlayerController.uc

  1. class TestPlayerController extends UTPlayerController;
  2. // Mouse event enum
  3. enum EMouseEvent
  4. {
  5.   LeftMouseButton,
  6.   RightMouseButton,
  7.   MiddleMouseButton,
  8.   ScrollWheelUp,
  9.   ScrollWheelDown,
  10. };
  11. var vector PlaneHitPos;
  12. var bool bRooted;
  13. var float PreviousSpeed;
  14. // Handle mouse inputs.
  15. function HandleMouseInput(EMouseEvent MouseEvent, EInputEvent InputEvent)
  16. {
  17.   local MouseInterfaceHUD MouseInterfaceHUD;
  18.   // Type cast to get our HUD
  19.   MouseInterfaceHUD = MouseInterfaceHUD(myHUD);
  20.   if (MouseInterfaceHUD != None)
  21.   {
  22.     // Detect what kind of input this is
  23.     if (InputEvent == IE_Pressed)
  24.     {
  25.       // Handle pressed event
  26.       switch (MouseEvent)
  27.       {
  28.         case LeftMouseButton:
  29.      MouseInterfaceHUD.PendingLeftPressed = true;
  30.      break;
  31.    case RightMouseButton:
  32.      MouseInterfaceHUD.PendingRightPressed = true;
  33.      break;
  34.    case MiddleMouseButton:
  35.      MouseInterfaceHUD.PendingMiddlePressed = true;
  36.      break;
  37.    case ScrollWheelUp:
  38.      MouseInterfaceHUD.PendingScrollUp = true;
  39.      break;
  40.    case ScrollWheelDown:
  41.      MouseInterfaceHUD.PendingScrollDown = true;
  42.      break;
  43.    default:
  44.      break;
  45.       }
  46.     }
  47.     else if (InputEvent == IE_Released)
  48.     {
  49.       // Handle released event
  50.       switch (MouseEvent)
  51.       {
  52.         case LeftMouseButton:
  53.      MouseInterfaceHUD.PendingLeftReleased = true;
  54.      break;
  55.    case RightMouseButton:
  56.      MouseInterfaceHUD.PendingRightReleased = true;
  57.      break;
  58.    case MiddleMouseButton:
  59.      MouseInterfaceHUD.PendingMiddleReleased = true;
  60.      break;
  61.    default:
  62.      break;
  63.       }
  64.     }
  65.   }
  66. }
  67. // Hook used for the left and right mouse button when pressed
  68. exec function StartFire(optional byte FireModeNum)
  69. {
  70.   HandleMouseInput((FireModeNum == 0) ? LeftMouseButton : RightMouseButton, IE_Pressed);
  71.   Super.StartFire(FireModeNum);
  72. }
  73. // Hook used for the left and right mouse button when released
  74. exec function StopFire(optional byte FireModeNum)
  75. {
  76.   HandleMouseInput((FireModeNum == 0) ? LeftMouseButton : RightMouseButton, IE_Released);
  77.   Super.StopFire(FireModeNum);
  78. }
  79. // Called when the middle mouse button is pressed
  80. exec function MiddleMousePressed()
  81. {
  82.   HandleMouseInput(MiddleMouseButton, IE_Pressed);
  83. }
  84. // Called when the middle mouse button is released
  85. exec function MiddleMouseReleased()
  86. {
  87.   HandleMouseInput(MiddleMouseButton, IE_Released);
  88. }
  89. // Called when the middle mouse wheel is scrolled up
  90. exec function MiddleMouseScrollUp()
  91. {
  92.   HandleMouseInput(ScrollWheelUp, IE_Pressed);
  93. }
  94. // Called when the middle mouse wheel is scrolled down
  95. exec function MiddleMouseScrollDown()
  96. {
  97.   HandleMouseInput(ScrollWheelDown, IE_Pressed);
  98. }
  99. // Override this state because StartFire isn’t called globally when in this function
  100. auto state PlayerWaiting
  101. {
  102.   exec function StartFire(optional byte FireModeNum)
  103.   {
  104.     Global.StartFire(FireModeNum);
  105.   }
  106. }
  107. //This is where the magic happens! This updates the pawn’s rotation to face the mouse cursor!
  108. function UpdateRotation(float DeltaTime){
  109.    local Rotator   targetRotation;
  110.    local Vector    aimStartPos;
  111.    local float     hitLength;
  112.    local MouseInterfaceHUD   playerHud;
  113.    local TestPawn  TestPawn;
  114.    if (self.Pawn != None)
  115.    {
  116.       TestPawn = TestPawn(self.Pawn);
  117.         if (TestPawn != none)
  118.          {
  119.              aimStartPos = TestPawn.GetAimStartLocation();
  120.          }
  121.          else
  122.         {
  123.              aimStartPos = self.Pawn.GetPawnViewLocation();
  124.          }
  125.          playerHud = MouseInterfaceHUD(myHUD);
  126.          // calc the player/world plane > deproj mouse ray intersection
  127.          hitLength = -((vect(0,0,1) dot (playerHud.CachedMouseWorldOrigin – aimStartPos)) / (vect(0,0,1) dot playerHud.CachedMouseWorldDirection));
  128.          PlaneHitPos = (playerHud.CachedMouseWorldOrigin + playerHud.CachedMouseWorldDirection * hitLength);
  129.          targetRotation = pawn.GetBaseAimRotation();
  130.          targetRotation.Yaw = rotator(PlaneHitPos – aimStartPos).Yaw; // only update yaw
  131.          Pawn.FaceRotation(targetRotation, DeltaTime);
  132.          }
  133.          else
  134.          {
  135.          super.UpdateRotation(DeltaTime);
  136.          }
  137. }
  138. //The adjustments to this state cause WASD to function correctly.
  139. state PlayerWalking
  140. {
  141. ignores SeePlayer, HearNoise, Bump;
  142.    function ProcessMove(float DeltaTime, vector NewAccel, eDoubleClickDir DoubleClickMove, rotator DeltaRot)
  143.    {
  144.       if( Pawn == None )
  145.       {
  146.          return;
  147.       }
  148.       if (Role == ROLE_Authority)
  149.       {
  150.          // Update ViewPitch for remote clients
  151.          Pawn.SetRemoteViewPitch( Rotation.Pitch );
  152.       }
  153.       //Forces WASD to work properly!
  154.       Pawn.Acceleration.X = 1 * PlayerInput.aBaseY * DeltaTime * 100 * PlayerInput.MoveForwardSpeed;;
  155.       Pawn.Acceleration.Y = 1 * PlayerInput.aStrafe * DeltaTime * 100 * PlayerInput.MoveForwardSpeed;
  156.       Pawn.Acceleration.Z = 0;
  157.       CheckJumpOrDuck();
  158.    }
  159. }
  160. defaultproperties
  161. {
  162.   // Set the input class to the mouse interface player input
  163.   InputClass=class’MouseInterfacePlayerInput’
  164. }

Mouseinterface

This is taken from the Mouse Interface Gem from the UDN.
They can probably explain it better and in more detail than me :)

MouseInterfaceGfx.uc

  1. class MouseInterfaceGFx extends GFxMoviePlayer;
  2. var MouseInterfaceHUD MouseInterfaceHUD;
  3. function Init(optional LocalPlayer LocalPlayer)
  4. {
  5.         // Initialize the ScaleForm movie
  6.         Super.Init(LocalPlayer);
  7.         Start();
  8.     Advance(0);
  9. }
  10. event UpdateMousePosition(float X, float Y)
  11. {
  12.         local MouseInterfacePlayerInput MouseInterfacePlayerInput;
  13.         if (MouseInterfaceHUD != None && MouseInterfaceHUD.PlayerOwner != None)
  14.         {
  15.                 MouseInterfacePlayerInput = MouseInterfacePlayerInput(MouseInterfaceHUD.PlayerOwner.PlayerInput);
  16.                 if (MouseInterfacePlayerInput != None)
  17.                 {
  18.                         MouseInterfacePlayerInput.SetMousePosition(X, Y);
  19.                 }
  20.         }
  21. }
  22. defaultproperties
  23. {
  24.     bDisplayWithHudOff=false
  25.     TimingMode=TM_Game
  26.     bPauseGameWhileActive=false
  27. }

HUD

MouseInterfaceHUD.uc

  1. class MouseInterfaceHUD extends HUD;
  2. // The texture which represents the cursor on the screen
  3. var Texture2D CursorTexture;
  4. // The color of the cursor
  5. var const Color CursorColor;
  6. // Pending left mouse button pressed event
  7. var bool PendingLeftPressed;
  8. // Pending left mouse button released event
  9. var bool PendingLeftReleased;
  10. // Pending right mouse button pressed event
  11. var bool PendingRightPressed;
  12. // Pending right mouse button released event
  13. var bool PendingRightReleased;
  14. // Pending middle mouse button pressed event
  15. var bool PendingMiddlePressed;
  16. // Pending middle mouse button released event
  17. var bool PendingMiddleReleased;
  18. // Pending mouse wheel scroll up event
  19. var bool PendingScrollUp;
  20. // Pending mouse wheel scroll down event
  21. var bool PendingScrollDown;
  22. // Cached mouse world origin
  23. var Vector CachedMouseWorldOrigin;
  24. // Cached mouse world direction
  25. var Vector CachedMouseWorldDirection;
  26. // Last mouse interaction interface
  27. var MouseInterfaceInteractionInterface LastMouseInteractionInterface;
  28. // Use ScaleForm?
  29. var bool UsingScaleForm;
  30. // Scaleform mouse movie
  31. var MouseInterfaceGFx MouseInterfaceGFx;
  32. simulated event PostBeginPlay()
  33. {
  34.   Super.PostBeginPlay();
  35.   // If we are using ScaleForm, then create the ScaleForm movie
  36.   if (UsingScaleForm)
  37.   {
  38.     MouseInterfaceGFx = new () class’MouseInterfaceGFx’;
  39.     if (MouseInterfaceGFx != None)
  40.     {
  41.       MouseInterfaceGFx.MouseInterfaceHUD = Self;
  42.       MouseInterfaceGFx.SetTimingMode(TM_Game);
  43.       MouseInterfaceGFx.Init(class’Engine’.static.GetEngine().GamePlayers[MouseInterfaceGFx.LocalPlayerOwnerIndex]);
  44.     }
  45.   }
  46. }
  47. simulated event Destroyed()
  48. {
  49.   Super.Destroyed();
  50.   // If the ScaleForm movie exists, then destroy it
  51.   if (MouseInterfaceGFx != None)
  52.   {
  53.     MouseInterfaceGFx.Close(true);
  54.     MouseInterfaceGFx = None;
  55.   }
  56. }
  57. function PreCalcValues()
  58. {
  59.   Super.PreCalcValues();
  60.   // If the ScaleForm movie exists, then reset it’s viewport, scale mode and alignment to match the
  61.   // screen resolution
  62.   if (MouseInterfaceGFx != None)
  63.   {
  64.     MouseInterfaceGFx.SetViewport(0, 0, SizeX, SizeY);
  65.     MouseInterfaceGFx.SetViewScaleMode(SM_NoScale);
  66.     MouseInterfaceGFx.SetAlignment(Align_TopLeft);
  67.   }
  68. }
  69. /*simulated function SwitchCursorTexture(Texture2D Cursor)
  70. {
  71. local MouseInterfacePlayerInput MouseInterfacePlayerInput;
  72.  // Ensure that we aren’t using ScaleForm and that we have a valid cursor
  73.   if (!UsingScaleForm && CursorTexture != None)
  74.   {
  75.     // Ensure that we have a valid PlayerOwner
  76.     if (PlayerOwner != None)
  77.     {
  78.       // If we’re not using scale form and we have a valid cursor texture, render it
  79.       if (MouseInterfacePlayerInput != None)
  80.       {
  81.         Canvas.DrawTile(Cursor, CursorTexture.SizeX, CursorTexture.SizeY, 0.f, 0.f, CursorTexture.SizeX, CursorTexture.SizeY,, true);
  82.       }
  83.     }
  84.   }
  85. }       */
  86. event PostRender()
  87. {
  88.   local MouseInterfacePlayerInput MouseInterfacePlayerInput;
  89.   local MouseInterfaceInteractionInterface MouseInteractionInterface;
  90.   local Vector HitLocation, HitNormal;
  91.   Super.PostRender();
  92.   // Ensure that we aren’t using ScaleForm and that we have a valid cursor
  93.   if (!UsingScaleForm && CursorTexture != None)
  94.   {
  95.     // Ensure that we have a valid PlayerOwner
  96.     if (PlayerOwner != None)
  97.     {
  98.       // Cast to get the MouseInterfacePlayerInput
  99.       MouseInterfacePlayerInput = MouseInterfacePlayerInput(PlayerOwner.PlayerInput)  ;
  100.       // If we’re not using scale form and we have a valid cursor texture, render it
  101.       if (MouseInterfacePlayerInput != None)
  102.       {
  103.         // Set the canvas position to the mouse position
  104.         Canvas.SetPos(MouseInterfacePlayerInput.MousePosition.X, MouseInterfacePlayerInput.MousePosition.Y);
  105.         // Set the cursor color
  106.         Canvas.DrawColor = CursorColor;
  107.         // Draw the texture on the screen
  108.         Canvas.DrawTile(CursorTexture, CursorTexture.SizeX, CursorTexture.SizeY, 0.f, 0.f, CursorTexture.SizeX, CursorTexture.SizeY,, true);
  109.       }
  110.     }
  111.   }
  112.   // Get the current mouse interaction interface
  113.   MouseInteractionInterface = GetMouseActor(HitLocation, HitNormal);
  114.   // Handle mouse over and mouse out
  115.   // Did we previously had a mouse interaction interface?
  116.   if (LastMouseInteractionInterface != None)
  117.   {
  118.     // If the last mouse interaction interface differs to the current mouse interaction
  119.     if (LastMouseInteractionInterface != MouseInteractionInterface)
  120.     {
  121.       // Call the mouse out function
  122.       LastMouseInteractionInterface.MouseOut(CachedMouseWorldOrigin, CachedMouseWorldDirection);
  123.       // Assign the new mouse interaction interface
  124.       LastMouseInteractionInterface = MouseInteractionInterface;
  125.       // If the last mouse interaction interface is not none
  126.       if (LastMouseInteractionInterface != None)
  127.       {
  128.         // Call the mouse over function
  129.         LastMouseInteractionInterface.MouseOver(CachedMouseWorldOrigin, CachedMouseWorldDirection); // Call mouse over
  130.       }
  131.     }
  132.   }
  133.   else if (MouseInteractionInterface != None)
  134.   {
  135.     // Assign the new mouse interaction interface
  136.     LastMouseInteractionInterface = MouseInteractionInterface;
  137.     // Call the mouse over function
  138.     LastMouseInteractionInterface.MouseOver(CachedMouseWorldOrigin, CachedMouseWorldDirection);
  139.   }
  140.   if (LastMouseInteractionInterface != None)
  141.   {
  142.     // Handle left mouse button
  143.     if (PendingLeftPressed)
  144.     {
  145.       if (PendingLeftReleased)
  146.       {
  147.         // This is a left click, so discard
  148.         PendingLeftPressed = false;
  149.         PendingLeftReleased = false;
  150.       }
  151.       else
  152.       {
  153.         // Left is pressed
  154.         PendingLeftPressed = false;
  155.         LastMouseInteractionInterface.MouseLeftPressed(CachedMouseWorldOrigin, CachedMouseWorldDirection, HitLocation, HitNormal);
  156.       }
  157.     }
  158.     else if (PendingLeftReleased)
  159.     {
  160.       // Left is released
  161.       PendingLeftReleased = false;
  162.       LastMouseInteractionInterface.MouseLeftReleased(CachedMouseWorldOrigin, CachedMouseWorldDirection);
  163.     }
  164.     // Handle right mouse button
  165.     if (PendingRightPressed)
  166.     {
  167.       if (PendingRightReleased)
  168.       {
  169.         // This is a right click, so discard
  170.         PendingRightPressed = false;
  171.         PendingRightReleased = false;
  172.       }
  173.       else
  174.       {
  175.         // Right is pressed
  176.         PendingRightPressed = false;
  177.         LastMouseInteractionInterface.MouseRightPressed(CachedMouseWorldOrigin, CachedMouseWorldDirection, HitLocation, HitNormal);
  178.       }
  179.     }
  180.     else if (PendingRightReleased)
  181.     {
  182.       // Right is released
  183.       PendingRightReleased = false;
  184.       LastMouseInteractionInterface.MouseRightReleased(CachedMouseWorldOrigin, CachedMouseWorldDirection);
  185.     }
  186.     // Handle middle mouse button
  187.     if (PendingMiddlePressed)
  188.     {
  189.       if (PendingMiddleReleased)
  190.       {
  191.         // This is a middle click, so discard
  192.         PendingMiddlePressed = false;
  193.         PendingMiddleReleased = false;
  194.       }
  195.       else
  196.       {
  197.         // Middle is pressed
  198.         PendingMiddlePressed = false;
  199.         LastMouseInteractionInterface.MouseMiddlePressed(CachedMouseWorldOrigin, CachedMouseWorldDirection, HitLocation, HitNormal);
  200.       }
  201.     }
  202.     else if (PendingMiddleReleased)
  203.     {
  204.       PendingMiddleReleased = false;
  205.       LastMouseInteractionInterface.MouseMiddleReleased(CachedMouseWorldOrigin, CachedMouseWorldDirection);
  206.     }
  207.     // Handle middle mouse button scroll up
  208.     if (PendingScrollUp)
  209.     {
  210.       PendingScrollUp = false;
  211.       LastMouseInteractionInterface.MouseScrollUp(CachedMouseWorldOrigin, CachedMouseWorldDirection);
  212.     }
  213.     // Handle middle mouse button scroll down
  214.     if (PendingScrollDown)
  215.     {
  216.       PendingScrollDown = false;
  217.       LastMouseInteractionInterface.MouseScrollDown(CachedMouseWorldOrigin, CachedMouseWorldDirection);
  218.     }
  219.   }
  220. }
  221. function MouseInterfaceInteractionInterface GetMouseActor(optional out Vector HitLocation, optional out VectorHitNormal)
  222. {
  223.   local MouseInterfaceInteractionInterface MouseInteractionInterface;
  224.   local MouseInterfacePlayerInput MouseInterfacePlayerInput;
  225.   local Vector2D MousePosition;
  226.   local Actor HitActor;
  227.   // Ensure that we have a valid canvas and player owner
  228.   if (Canvas == None || PlayerOwner == None)
  229.   {
  230.     return None;
  231.   }
  232.   // Type cast to get the new player input
  233.   MouseInterfacePlayerInput = MouseInterfacePlayerInput(PlayerOwner.PlayerInput)  ;
  234.   // Ensure that the player input is valid
  235.   if (MouseInterfacePlayerInput == None)
  236.   {
  237.     return None;
  238.   }
  239.   // We stored the mouse position as an IntPoint, but it’s needed as a Vector2D
  240.   MousePosition.X = MouseInterfacePlayerInput.MousePosition.X;
  241.   MousePosition.Y = MouseInterfacePlayerInput.MousePosition.Y;
  242.   // Deproject the mouse position and store it in the cached vectors
  243.   Canvas.DeProject(MousePosition, CachedMouseWorldOrigin, CachedMouseWorldDirection);
  244.   // Perform a trace actor interator. An interator is used so that we get the top most mouse interaction
  245.   // interface. This covers cases when other traceable objects (such as static meshes) are above mouse
  246.   // interaction interfaces.
  247.   ForEach TraceActors(class’Actor’, HitActor, HitLocation, HitNormal, CachedMouseWorldOrigin +CachedMouseWorldDirection * 65536.f, CachedMouseWorldOrigin,,, TRACEFLAG_Bullet)
  248.   {
  249.     // Type cast to see if the HitActor implements that mouse interaction interface
  250.     MouseInteractionInterface = MouseInterfaceInteractionInterface(HitActor);
  251.     if (MouseInteractionInterface != None)
  252.     {
  253.       return MouseInteractionInterface;
  254.     }
  255.   }
  256.   return None;
  257. }
  258. function Vector GetMouseWorldLocation()
  259. {
  260.   local MouseInterfacePlayerInput MouseInterfacePlayerInput;
  261.   local Vector2D MousePosition;
  262.   local Vector MouseWorldOrigin, MouseWorldDirection, HitLocation, HitNormal;
  263.   // Ensure that we have a valid canvas and player owner
  264.   if (Canvas == None || PlayerOwner == None)
  265.   {
  266.     return Vect(0, 0, 0);
  267.   }
  268.   // Type cast to get the new player input
  269.   MouseInterfacePlayerInput = MouseInterfacePlayerInput(PlayerOwner.PlayerInput)  ;
  270.   // Ensure that the player input is valid
  271.   if (MouseInterfacePlayerInput == None)
  272.   {
  273.     return Vect(0, 0, 0);
  274.   }
  275.   // We stored the mouse position as an IntPoint, but it’s needed as a Vector2D
  276.   MousePosition.X = MouseInterfacePlayerInput.MousePosition.X;
  277.   MousePosition.Y = MouseInterfacePlayerInput.MousePosition.Y;
  278.   // Deproject the mouse position and store it in the cached vectors
  279.   Canvas.DeProject(MousePosition, MouseWorldOrigin, MouseWorldDirection);
  280.   // Perform a trace to get the actual mouse world location.
  281.   Trace(HitLocation, HitNormal, MouseWorldOrigin + MouseWorldDirection * 65536.f, MouseWorldOrigin , true,,, TRACEFLAG_Bullet);
  282.   return HitLocation;
  283. }
  284. defaultproperties
  285. {
  286.   // Set to false if you wish to use Unreal’s player input to retrieve the mouse coordinates
  287.   UsingScaleForm=false
  288.   CursorColor=(R=255,G=255,B=255,A=255)
  289.   CursorTexture=Texture2D’EngineResources.Cursors.Arrow’
  290. }

MouseINteraction

MouseInterfaceInteractionInterface.uc

  1. interface MouseInterfaceInteractionInterface;
  2. // Called when the left mouse button is pressed
  3. function MouseLeftPressed(Vector MouseWorldOrigin, Vector MouseWorldDirection, Vector HitLocation, VectorHitNormal);
  4. // Called when the left mouse button is released
  5. function MouseLeftReleased(Vector MouseWorldOrigin, Vector MouseWorldDirection);
  6. // Called when the right mouse button is pressed
  7. function MouseRightPressed(Vector MouseWorldOrigin, Vector MouseWorldDirection, Vector HitLocation, VectorHitNormal);
  8. // Called when the right mouse button is released
  9. function MouseRightReleased(Vector MouseWorldOrigin, Vector MouseWorldDirection);
  10. // Called when the middle mouse button is pressed
  11. function MouseMiddlePressed(Vector MouseWorldOrigin, Vector MouseWorldDirection, Vector HitLocation, VectorHitNormal);
  12. // Called when the middle mouse button is released
  13. function MouseMiddleReleased(Vector MouseWorldOrigin, Vector MouseWorldDirection);
  14. // Called when the middle mouse button is scrolled up
  15. function MouseScrollUp(Vector MouseWorldOrigin, Vector MouseWorldDirection);
  16. // Called when the middle mouse button is scrolled down
  17. function MouseScrollDown(Vector MouseWorldOrigin, Vector MouseWorldDirection);
  18. // Called when the mouse is moved over the actor
  19. function MouseOver(Vector MouseWorldOrigin, Vector MouseWorldDirection);
  20. // Called when the mouse is moved out from the actor (when it was previously over it)
  21. function MouseOut(Vector MouseWorldOrigin, Vector MouseWorldDirection);
  22. // Returns the hit location of the mouse trace
  23. function Vector GetHitLocation();
  24. // Returns the hit normal of the mouse trace
  25. function Vector GetHitNormal();
  26. // Returns the mouse world origin calculated by the deprojection within the canvas
  27. function Vector GetMouseWorldOrigin();
  28. // Returns the mouse world direction calculated by the deprojection within the canvas
  29. function Vector GetMouseWorldDirection();

Player Input

MouseInterfacePlayerInput.uc

  1. class MouseInterfacePlayerInput extends PlayerInput;
  2. var PrivateWrite IntPoint MousePosition;
  3. event PlayerInput(float DeltaTime)
  4. {
  5.         local MouseInterfaceHUD MouseInterfaceHUD;
  6.         // Handle mouse movement
  7.         // Check that we have the appropriate HUD class
  8.         MouseInterfaceHUD = MouseInterfaceHUD(MyHUD);
  9.         if (MouseInterfaceHUD != None)
  10.         {
  11.                 if (!MouseInterfaceHUD.UsingScaleForm)
  12.                 {
  13.                         // If we are not using ScaleForm, then read the mouse input directly
  14.                         // Add the aMouseX to the mouse position and clamp it within the viewport width
  15.                         MousePosition.X = Clamp(MousePosition.X + aMouseX, 0, MouseInterfaceHUD.SizeX);
  16.                         // Add the aMouseY to the mouse position and clamp it within the viewport height
  17.                         MousePosition.Y = Clamp(MousePosition.Y – aMouseY, 0, MouseInterfaceHUD.SizeY);
  18.                 }
  19.         }
  20.         Super.PlayerInput(DeltaTime);
  21. }
  22. function SetMousePosition(int X, int Y)
  23. {
  24.         if (MyHUD != None)
  25.         {
  26.                 MousePosition.X = Clamp(X, 0, MyHUD.SizeX);
  27.                 MousePosition.Y = Clamp(Y, 0, MyHUD.SizeY);
  28.         }
  29. }
  30. defaultproperties
  31. {
  32. }

Compile, debug and there you go!

I have tried to extend from UTGame instead of GameInfo to save some work on the HUD. The only successful approach was to utilize Scaleform and use the startDrag() function within Action Script.
The mouse cursor got laggy and I have not gone deeper down that path.

Let me know if you do and have fun with the new Top Down shooter!

Sorry for the mess with the pasted code into WordPress.
Copy and Paste should still be fine.

Advertisements

Electrocute (II)

Goal:

We want to create an area of water that, when shot, deals damage to actors if they are within it.
The water area has a cooldown until it can deal damage again.
The player can be hurt as well.
Bots will play a special animation when taking damage from water.

Core Nodes:

  • Modify Health
  • Pawn Anim
  • Modify Object List

Complete Kismet

Electrocute

HEADS UP:

If you are not finding the Node you want to create simply click on the Light bulb icon in the Kismet upper interface. A new window will open where you can type in the name of the Node you want to create.

If you are wondering about the connection of each Node then please check the screen shots.

01. The water

First we want to create an area that can be touched and shot at, the water.

In a new UDK Map we can select the block in the middle of the map, hold down the alt key and drag a copy to a new place.
We set the DrawScale for X to 2.0 for Y to 2.0 and for Z to 0.01.
We move the mesh down to the ground but not into the ground. Actors still have to be able to walk on it.
We convert the mesh to a K Actor by right-clicking the mesh -> convert to K Actor.
Now it is able to take damage.
We open the K Actor’s properties by hitting F4.
We disable “Damage applies Impulse”.
We set the Collision Type to “COLLIDE_TouchAll”.
We open the Content Browser, select Materials and search for “water“.
We choose a fitting Material and select it with a left click.
We right-click the K Actor, choose Materials and Assign Material from Content Browser.

Now we have an area that we can walk on, shoot and that looks something like water :).

WaterWater Properties

02. Touching the Water

Now we have to set up the logic to identify actors that touch the water.

We select the K Actor and open Kismet.
We select “New Event using KActor_0 -> Touch.
We set the Max Trigger Count to 0. Now the Event can be fired infinitely.
We set the Retrigger Delay to 0.
We create a new Object Variable as Instigator and name it “Water Toucher“.

We create a Modify Object List node.
We link the “Touched” Output to the “Add To List” Input.
We link the “UnTouched” Output to the “Remove From List” Input.
As Object Reference we set the Water Toucher.
We create a new Object List Variable and name it “Water Touchers“.
We set the “Water Touchers” as Object List Variable of the node.

Now we collect all actors that touch the water within the Water Touchers List.
If an actors leaves the water, he is removed from this list.

Touching Water

03. Applying Damage

Now we want to deal damage to the actors that touch the water when we shot it.

We select the K Actor.
In Kismet we select “New Event using KActor_0 -> Take Damage”.
We set the Damage Threshold to 1. The water will deal damage once it took 1 point of damage.
We set the Max Trigger Count to 0.
We set the Retrigger Delay to 0.5. This is the cool down. After 0.5 seconds the water will deal damage again, when shot.
We create a Modify Health node.
We set the Momentum to 0.
We set the Amount to 75. This is the amount of damage that will be dealt.
As Target we set the Water Touchers List.
As Instigator we set the Player.
We create a Pawn Anim node.
We open the Content Browser and tick only the Animation Sets.
We double click “K_AnimHuman_BaseMale“.
We select the Anim tab, select “Death_Stinger” from the list and copy the name to the clipboard by right clicking.
In the Pawn Anim node we pasteDeath_Stinger” as Anim Name.
As Target we set the Water Touchers List.

Now when the water takes 1 point of damage, all Water Touchers will take 75 points of damage and play the Death_Stinger animation.
The water will deal damage again when shot again after 0.5 seconds.

Water Damage

Ok, this was a quick but useful one.
I hope you will have some fun with it.
Thanks!

Exploding Barrels (I)

Goal:

We want to create an Object that explodes when taking damage and sets Enemies on fire.
Burning enemies will lose health over time.
Burning
enemies will have a fire attached to them.

Core Nodes:

  • Modify Health
  • Access / Modify Object List
  • Actor Factory Ex
  • Touch Event

Complete Kismet

Complete Kismet

HEADS UP:

If you are not finding the Node you want to create simply click on the Light bulb icon in the Kismet upper interface. A new window will open where you can type in the name of the Node you want to create.

If you are wondering about the connection of each Node then please check the screen shots.

step 01: The Barrel and the explosion

At first we have to create an Object that explodes when shot.
You can use any static mesh you see fit, I will use a barrel (make sure the Object is fully loaded).

We choose a static mesh from the content browser and add it as Interp Actor.
We make sure that the Collision for the Object is set to “Block All“. Otherwise our projectiles will not hit the static mesh and therefore won’t do any damage.
We select the Interp Actor and create a new Take Damage Event using that Actor in Kismet.
We set the Damage Threshold to 75.
We set the Max Trigger Count to 0. We do this for testing purposes.
We set the Retrigger Delay to 0.5. Again, we do this for testing purposes.

Now we choose an emitter for the explosion and set when to trigger the emitter.
We choose a fitting Explosion Emitter from the Content Browser.
We create a Toggle node.
We use the Turn On Input.
As Target we set the Emitter.
We create a Remote Event and name it “On Fire“.
We activate the remote Event after the Toggle node.

Now when the Barrel has taken 75 Damage the Explosion Emitter is toggled and the Remote EventOn Fire” is triggered.

Take Damage Event which triggers an emitter and a remote Event.

STEP 02: The Explosion Radius

Now we want to set a Radius in which Enemies are set on fire if they are within the Radius at the time of the Explosion.

We create a Trigger Volume around the Barrel.
We select the Trigger Volume and create a Touch Event using the Trigger Volume.
We set the Max Trigger Count to 0.
We set the Retrigger Delay to 0.
We uncheck the Player Only checkbox.
As Instigator we create a new Object Variable and name it “Inside Barrel Radius“.

Now we gather all actors touching the Barrel Radius in a list.
We create a Modify Object List.
As Object Reference we set the “Inside Barrel RadiusInstigator.
We create a Object List and name it “Barrel Radius List“.
As Object List Variable we create a named Variable of the “Barrel Radius List“.
We use the Touch output to add actors to the “Barrel Radius List”.
We use the UnTouched output to remove actors from the “Barrel Radius List”.

Adding / Removing actors from the Barrel Radius.

Adding / Removing actors from the Barrel Radius.

Step 03: Damage Takers list

No we will take care of the “On Fire” event.
At first we need to shift the actors within the “Barrel Radius List” into a new list.

We use the Remote Event “On Fire” as starting point.
We create an Access Object List node.
We use the “First” Input.
As Object List we use the “Barrel Radius List”.
As Output Object we create a new Object Variable and name it “Bot on Fire“.
We create a Modify Object List node.
We use the Add to List Input.
As Object Reference we use the “Bot on Fire“.
As Object List we create a new Object List and name it “Damage Takers“.

Now we want to remove the “Bot on Fire” from the “Barrel Radius List”. We do this in order to loop the sequence later on.
We create a Modify Object List node.
We use the Remove from List Input.
As Object Reference we use the “Bot on Fire“.
As Object List we use the Barrel Radius List.

Step 04: Visual Feedback – The fire

It is time to set stuff on fire.
We want to set every actor, who was inside the barrel radius at the time of the explosion, alight.

First we will create an emitter for every actor within the list.
We create a Delay node and set it to 0.1
We create an Actor Factory Ex node.
We use the Finished output from the Delay node and link it to the Spawn Actor Input.
We use the Out output from the Modify Object List and link it to the Enable input.
As Factory we use ActorFactoryEmitter.
In the Content Browser we search a fitting Particle System and add it in the Actor Factory.
We set the Spawn Delay to 0.1
As Spawn Point we use the “Bot on Fire“.
As Spawned 1 Output we create a new Object Variable and name it “Bot Fire“.
We create 2 Log nodes and link the Finished output via the logs to the Disable input.

The Actor Factory Ex for visual feedback.

The Actor Factory Ex for visual feedback.

The emitter needs to stick to each actor.
We create a Attach to Actor node.
As Target we set the “Bot on Fire“.
As Attachment we set the “Bot Fire“.
Now the Emitter will follow the actor.

We also add the emitter to a list.
Why we do this will be explained in the second part of the tutorial. We don’t need it for the goals, we established in the beginning.
We create a Modify Object List node.
We use the Add to List input.
As Object Reference we set the “Bot Fire“.
We create a new Object List and name it “Fire List“.
As Object List Variable we use a named Variable of the “Fire List“.

Attaching the Fire to the actor.

Attaching the Fire to the actor.

Step 05: The loop

As we don’t want to check every actor individually we loop this whole sequence.
The lists will take the first entry of the “Barrel Radius List”, go through the script and then remove the first entry from the list, so the next actor becomes the first entry.
This behavior is looped until every actor within the list has had it’s turn and the list is empty.

We create 2 Log nodes and link the Out output of the Modify Object List (Fire List) to the First input of the Access Object List (Barrel Radius List).
Boom.

Now when the barrel explodes, all actors within it’s radius will be set alight.

The Loop

Step 06: Fire Damage

The burning fire should of course also apply damage to the actors.
We will go for a loop of low damage.

We create a Modify Health node.
We set the Momentum to 0.
We set the Amount to 4.
As Target we set the “Damage Takers” List.
As Instigator we set the Player.

Now all actors from the “Damage Takers” List will once take 4 Damage.

We create a Delay node.
We set the Amount to 0.5.
We create a Log node and link the “Finished” Output of the Delay to the Input of the Log.
We close the Loop by linking the Ouput of the Log into the Input of the Modify Health node.

Now all actors from the “Damage Takers” List will take 4 Damage every 0.5 seconds.

Looping the fire damage.

Looping the fire damage.

That’s it!

The second part of this tutorial will explain how to allow the actors to put out the fire (water), therefore also stopping the damage.
It will also contain a new possibility how to deal environmental damage which will create a fitting dynamic with the explosions and fire we have just created.

Thanks and have fun!

Free to Play: Monetization

With the upcoming change of Star Wars: The Old Republic from its subscription model to free to play, the increasing interest in this business model from game developers outside the social and mobile segment and the ongoing success of titles like Riot’s League of Legends or Valve’s Team Fortress 2, it is time to analyse the underlying monetization of virtual goods.

Team Fortress 2 Vanity Item and Steam Cross Promotion

The rise of free to play emphasizes flawless and innovative game design. When beginning a game is as easy as leaving a game, it becomes crucial to retain users, keep them interested and offer them variation on a constant basis.
While the game design and retention draw the player in and deliver fun, the monetization should amplify and reinforce this, never deter from it.

Virtual goods might still baffle non-gamer or even AAA developers, but they have become a billion dollar business and a major part of the mobile and social game design.
The idea of “buy to win” quickly comes to mind and there are still many games, especially in the social space, which have a poor, boring and robbing monetization.
So, let’s ask some questions.

Zynga’s revenues

Q: When should you take care of the monetization in your game?

Deciding to make a game that will generate revenues via the sale of virtual goods is an essential decision. This decision will be made before designing the game and rightly so.
The game design needs to plan and understand as early as possible what will be sold and how it will affect the game balance and experience.
If the monetization via selling virtual goods is added to a nearly finished game, it will feel tacked on and like extracting money out of the players without improving the game play. The best way to avoid this type of design afterthought is to carefully identify which parts of your game design could reasonably rely upon the sale of virtual goods, and which parts need to remain untangled from the influence of microtransactions.

Q: What categories of virtual goods are there? 

The best types of virtual goods are those which fit nicely into the game world, and act as a core component of each and every move. In a city-building game, each different building the user interacts with could be considered a virtual good. In an online driving game, the subcomponents of the user’s car can be the basic building blocks of the virtual goods system. For a first person shooter, each gun or bullet could be a purchasable item, and so on.

Spend time analyzing other games in the market and take advice from there; would Monopoly be as much fun if you could just throw a dollar on the board every time you wanted to buy a hotel? Determine what you’re going to sell for real money, and carefully integrate that plan into your overall game design.

You can separate virtual goods into two categories.
There are goods that tap into and alter the game play, the “Functional Advantages” and there are the purely aesthetic goods that don’t interfere with the game play and mechanics but which are often no less important, the “Vanity Items“.

Functional Advantages

Q: What are Functional Advantages?

There are many types of goods that can grant a Functional Advantage in a game. In a shooter you could buy a gun that simply does more damage or fires faster than the guns that are for free. In a role playing game you could buy gear or spells with special abilities or in a city builder you could by a bulldozer that lets you build faster or more efficiently than the cost-free one.

Game balance is always an important factor when selling items that grant a real advantage in terms of game play to the player. If rich players are overpowered, those players who spend less or as much will be disenfranchised and leave the game frustrated.
If the game is too easy or too hard, players will also abandon the game.

Q: Are there general issues about Functional Advantages?

  • Selling functional items in Player vs. Player (PvP) games can easily destroy the game balance. If players, who are willing to spend, simply can put in a few dollars in order to create such a powerful advantage that players who are not spending money cannot compete then they will move on and quit the game. This sensibility is particularly strong in North America, though less so in Asia based on the popularity of games like Crossfire and ZT Online.On the other hand the risk of frustrating players in the way described above can also become a major selling point. Players tend to get emotional and plan their revenge, using any means necessary. Still, using rage and revenge as revenue stream might not be the best idea, since it will build a nasty and unfriendly community.
  • When selling functional advantages in games where players fight against the environment, the so called PvE (e.g. AI, Bots, Monsters, etc.) the players might quickly feel like there is no skill needed to overcome the challenge. Plugging in money will always be a guaranteed victory.
  • However items that create a new variation of the game experience have the opportunity to become significant sellers in the PvE space. Offer the player new ways to overcome the game’s challenges but be sure not to take the challenge or the needed skill away. Items that save a little time, increase the player’s chances or add a new layer to the game play are items that vary the player’s already positive experience.

Q: Are there any tips when using Functional Advantages?

  • Limit the duration a purchased item can be used by any player. For instance players won’t be able to use the fast-firing gun indefinitely but only for a limited time. This lets you resell the item as a consumable and also helps to balance an item what might be overpowering against other players. Even the most vigilant will forget to “re-up” their item, or decide to try to play without it, etc. A set duration also allows your players to see the comparable value of the item. They are regularly reminded that the small expenditure increases the enjoyment of their gaming experience, or it allows them to spend their money on other items you might offer, thus increasing the variety of their game experience.
  • Create indirect advantages that are not obvious for the player’s opponents. For instance, imagine that you let the player buy an item that allows them greater speed in the construct of new buildings; rather than simply decrease the player’s time to build each building, consider designing the item instead to reduce the amount of lumber, or metal, or brick required to construct the building. This way, the increased speed at which a player can build isn’t patently obvious to their opponents, but your player still enjoys the same net effect (more buildings in the same time).
  • Most games that make use of a meta-game loop, like the experience system in Call of Duty or the summoner advancement model in League of Legends, sell accelerators that let players progress faster. It is less obvious than direct consumables that grant an advantage, which are often perceived as “cheating”.
  • Counter-moves or specific defenses are a great way to balance functional advantages. In a sports game, one player might have an item to increase the power of his tennis serve by 25% but the opponent might have an item that reduces all power bonuses to 10%.
    This way the player has still an advantage but the game offers tactical depth and players are able to adapt which reduces frustration and maintains a healthy level of competitiveness.
  • Add active involvement and interactivity to “super-killers”. Don’t limit the player’s action to the press of a button when he uses the nuke to quickly defeat all enemies. Instead use a similar weapon that still needs a little bit of skill or at least active involvement. Aiming and firing can be very easy but it increases the fun and reduces the feeling of buying the win. It is about the player’s credit card vs.the player feeling skillful.
  • Find dull tasks in your game (e.g. backtracking, selling useless loot) and offer an elegant solution. In NCSoft’s Dungeon Runners the player frequently stops fighting in order to head back to town and sell his loot. They introduced a little gnome that is accompanying the player who can turn loot into gold on the spot. The time saving as well as the funny presentation of the gnome made the feature popular.
    Of course you should never design dull tasks just to monetize them.
  • Offer items that slightly vary the game play and also add a visual difference. In a game about world exploration like Bethesda’s Skyrim, the player could choose between several mounts like a horse, a lion or different creatures. The mounts should not only differ in visual appearance but also vary the game experience. The horse could be able to sprint and run faster, while the lion is able to jump or climb hills.Another example is the Downloadable Content for Rocksteady’s Batman: Arkham City, where the player is able to play a new character, Catwoman. The game’s controls and combat system remain the same while Catwoman presents a whole new set of animations, slightly varied combat gadgets and a new way how to move through the city of Arkham.
  • Sell upgrades that make players more flexible, but not necessarily more powerful. Allowing players to carry two weapons instead of one, while others can only carry one weapon is not inherently unbalanced. Again it will bring variation to the players’ tactics allowing for depth and greater flexibility.

Q: How works the balancing of Functional Advantages in terms of pricing?

The game designer has to set the influence of the items. He has to consider how great the degree of deviation from the standard curve of in-game performance is. The greater the deviation from the standard curve the more expensive should the item be.

Vanity Items

We have talked about virtual goods which tap into the mechanics and shift the experience from a game play point of view.
The second category of virtual goods, the Vanity Items, doesn’t alter the game play at all. They bring variation to the visual sensation and allow the player to customize his experience. Allowing the player to customize every detail of his avatar, user interface and other game elements enables a very personalized experience.
As status is important to many people, these items tend to be very popular and big sellers.

A vanity skin from League of Legends

Q: What can be customized?

Games that feature a player avatar have many possibilities for customization. Everything from head to toe can be adjusted and, in common, the outstanding items will cost the player.

Especially items which mind the global marketplace can be successful. Players like to show their affinity with national flags, district insignia, sport team emblems or belief system logos. Items which allow players to identify themselves with interests, regions or other similarities are popular.

Depending on the flexibility of your game’s engine, store and back-end system you can also tailor items for seasonal and special events. Christmas hats will probably never go out of fashion and sports events (e.g. the Olympics, soccer world cup, etc.) become bigger and bigger.
Not only the well-known events are important. Regional events can be just as interesting as they are specifically targeted at a smaller audience and therefore more appealing to that audience.
Keep an eye on the calendar, think of the Chinese new year and the Oktoberfest just as well as the smaller, regional events.

Games that don’t feature player avatars can also be customized. The countless city building social games let the player decide whether to build the football stadium or the funfair. Racing games allow the player to build his own car out of single auto parts. Big MMOs like World of Warcraft let the player customize not only the player avatar but the user interface as well. Even in the Xbox Live version of Magic: The Gathering the players can buy special skins for their card decks.

Q: Can Vanity Items be designed for virality?

One of the big benefits from Vanity Items is that they are viral. Players buy these items in order to show them off. This makes Vanity Items much more visible to other players thus increasing the desire to possess one and reducing the barrier to buy one.

Some games also offer the players the possibility to create their own symbols, icons and skins. The upside of user generated content is a (hopefully) diligent community which is proud of their creations and ready to spend money on extra features to create unique emblems.
Be careful what tools you give your community access to, though. Swastikas, penises, etc. seem to be as tenacious as Christmas hats.

Planning from the start what options your players will have to customize their game experience is important. It is crucial to the look and feel of your game and it might also become very expensive and tedious to add detailed customization late in the development process.

Q: How to optimize monetization?

There are some best practices and marketing strategies in order to improve and optimize the monetization of your game.
Of course these practices are not universal remedies and every genre and game works differently and attracts different audiences.

  • Rarity is a concept often used when selling virtual goods. One example is the sale of an item which is limited in amount and by time. Selling the magical sword for 50 coins instead of 100 coins makes the player feel like he is making a profitable deal. Also adding a limiting factor (e.g. “Only 150 magical swords remain!”) puts a perceived scarcity on top, which makes the item more desirable.
  • Bundle purchases, discounts as well as the old “Buy 1 Get 1 Free” also spark the willingness to buy.
  • Viral promotions work much like the “Buy 1 Get 1 Free” sale but allow the player to gift the bought item to one of his friends which reinforces reciprocity.
  • Two currencies are often used in free to play games. The soft currency can be obtained normally by playing and the hard currency is obtained by paying money or playing the game extensively.
  • Rewarding your players with hard currency for achieving milestones in your game or for retention also increases the probability that they will buy virtual goods. It lowers the barrier to spend hard currency.
  • Discounts on buying currency as well as a payment page optimization are important. It helps to offer the player a best deal option when he is deciding to buy hard currency.
    The best deal option can be selected by default, be highlighted and you can even reward the player with an extra item for choosing the best deal option.
    Keeping the most important interactions (e.g. buying currency, choosing the amount of currency) visible and offering the most convenient paying options is also part of optimizing your game’s monetization.
  • The virtual storefront is just as important as the retail storefront.
    Proper merchandising and the highlighting of a featured virtual good within the interface will increase the awareness of the item and drive sales.

  • Another familiar concept is “Try before you buy“. The trick is to set a limited time in which your players experience the fun of a virtual good become used to it. The fear of losing what one is accustomed to can be quite big and if the item is useful and fun to players the might reconsider and buy it.

Naturally all of these concepts and practices can be combined with each other in order to achieve the greatest performance.
Tracking the monetization of your game and knowing the metrics can be just as important as creating meaningful virtual goods.

If you are interested in this topic, I would recommend these links for continued reading:

Thanks for reading.

Enemy: Cannibal

Goal:

We want to create an Enemy that moves to dead enemies and draws health out of them.

Core Nodes:

  • Modify Health
  • Death
  • Move to Actor
  • Object Lists

Complete Kismet

HEADS UP:

If you are not finding the Node you want to create simply click on the Light bulb icon in the Kismet upper interface. A new window will open where you can type in the name of the Node you want to create.

If you are wondering about the connection of each Node then please check the screen shots.

step 01: Spawning the Enemy

We create an Event to trigger the spawning of the Bot. For this case we will use the Event Player Spawn.
We create an Actory Factory Ex.
As Factory we set UTActorFactoryAI.
As Pawn Class we set UTPawn.
We name the Pawn “Cannibal“.
We set a Spawn Point for the Bot and name the Variable “Cannibal Spawn Point“.
We create the Bot Object Variable and name the Variable “Cannibal Bot“.

This is for spawning the Actor and AI.

Step 02: Spawning a Victim

We will repeat Step 01 and create a Bot that will serve as victim for the Cannibal Bot.
Repeat Step 01 and make sure to:
Use a different Pathnode than the Cannibal Bot.
Name the Bot “Victim 01“.
Create a Bot Object Variable and name it “Victim Bot 01“.

STEP 03: The Victim List

We have to gather all possible victims for the Cannibal Bot in order to tell him where to go so he can draw health out of a dead pawn.

We create a new Object List and call it “Victim List“. Make sure this is not linked to anything.
We create a Modify Object List Node.
We use the Add to List Input.
As Object Reference we use the Victim Bot Object Variable.
As Object List Variable we use a named VariableVictim List“.
Now we have added the Victim Bot to a List.

Step 04: Rinse and Repeat

We will repeat Step 02 and Step 03 in order to create another Victim. We do this to test the behaviour of the Cannibal Bot later on.

Be sure to:
Use a different Pathnode than Cannibal Bot and Victim Bot 01.
Name the Bot “Victim Bot 02“.
Create a Bot Object Variable and name it “Victim Bot 02“.
Add Victim Bot 02 to the Victim List.

STEP 05: Choosing a Victim

Now we want to check if a Victim Bot dies and mark that Bot as target for the Cannibal Bot.

We create an Attach to Event Node.
As Attachee we set the Victim List.
We create a Death Event.
We set the Max Trigger Count to 0.

Now we can tell the Bot to behave differently when a Victim Bot dies.
But how can we tell which Victim Bot died?

We use the Instigator from the Death Event, create a new Object Variable and call it “Cannibal Victim“.

Troubleshooting:
There might be problems with the Timing of the spawned Victims and the the Cannibal Bot checking the Victim List. (If the Cannibal Bot checks the Victim List before the Bots are spawned then he is basically checking an empty list and will do nothing.)

To prevent this we create a remote event that links in to the Attach to Event Node in order to “update” the Victim List.

We create a Remote Event and name it “Victim List Update“.
We create a Activate Remote Event Node and name it “Victim List Update“.
We link the Output of the Modify Object List Node of both Victim Bots to the Activate Remote Event Node.
Finally we link the Output of the Remote Event to the Attach to Event Node.

STEP 06: Only one Victim at a Time

Now we want to prevent the Cannibal Bot from running from one dead Victim to the next one before he has finished his behavior.

We create a Gate Node.
We check the Open checkbox.
We create 2 Log Nodes in order to close the Gate immediately after the Death Event is fired.
(Be sure to uncheck the Output to Screen checkbox from the Log Nodes if you are getting UI Click Sounds when they are activated ingame.)

STEP 06: Movement

We create a Move to Actor Node.
As Target we set the Cannibal Bot.
As Destination we set the Cannibal Victim.

STEP 07: Drawing Health Phase

We create a Delay Node.
We set the Duration to 3.

This is the place where we could do fancy feedback stuff.
We’ve created the delay so the Cannibal doesn’t gain health immediately.
We could as well add sounds, particles to support the feedback to the player.
We could also tell the Cannibal to stop his other AI behavior while being in this phase or to explode when he dies in this phase, etc.

STEP 08: Healing the Cannibal

We create a Modify Health Node.
We set the Momentum to 0.
We set the Amount to 75.
We check the Heal checkbox.
As Target we set the Cannibal Bot.

Now the Cannibal Bot is healed for 75 when the Delay node is finished.

Last Step: Reopen the Gate

In order to let the Cannibal Bot restart his behavior once a Victim dies we need to reopen the Gate we have created and closed after its use.

We create a Log (or Delay) Node.
We link the Output of the Log to the Open of the Gate.

That’s it! Now the Cannibal can restart his behaviour.

Thanks.

Enemy: Teleporter

Goal:

We want to create an Enemy that teleports himself to a random position when he takes damage.
The Enemy has more Health than a regular Bot.
The Enemy has infinite Ammo and a Rocket Launcher.

Core Nodes:

  • Teleport
  • Take Damage
  • Object Lists

Complete Kismet:

Heads Up:

If you are not finding the Node you want to create simply click on the Light bulb icon in the Kismet upper interface. A new window will open where you can type in the name of the Node you want to create.

If you are wondering about the connection of each Node then please check the screenshots.

step 01: Spawning the Enemy

We create an Event to trigger the spawning of the Bot. For this case we will use the Event Player Spawn.
We create an Actory Factory Ex.
As Factory we set UTActorFactoryAI.
As Pawn Class we set UTPawn.
We name the Pawn “Teleporter“.
We add an Inventory List Item and set it to Rocket Launcher.
We set a Spawn Point for the Bot and name the Variable “Teleporter Spawn Point“.
We create the Bot Object Variable and name the Variable “Teleporter Bot“.

This is for spawning the Actor and AI.

Step 02: Increasing the Maximum Health

We create a Modify Property Node.
We add a Property to modify.
The Property Name is “HealthMax“.
We check the Modify Property checkbox.
We set the Property Value to 300. This is the Maximum Health Value of the Target.
As Target we set the Teleporter Bot (I created a Named Variable in order to keep everything readable).

Now we have increased the maximum health of the Teleporter Bot to 300.

We create a Modify Health node.
We set the Momentum to 0.
We set the Amount to 300.
We check the Heal checkbox.
As Target we set the Teleporter Bot.

Without healing the Bot he would die after just as many hits as any other enemy.
So we have to heal the Bot in order to make use of his new maximum health.

Step 03: Infinite Ammo

We create an Infinite Ammo Node. Easy.
We make sure that the “Infinite Ammo checkbox is checked.
We create a Give Inventory Node.
We add an Inventory List Item and choose the Rocket Launcher. Again ;)
We check Clear Existing.
We check Force Replace.
As Target we set the Teleporter Bot.

Now the Bot has infinite Rocket Shots.
Oh boy.

Step 04: Teleport Points

We create several Pathnodes in our Level. These will be the points where the Teleporter can teleport to.
We create an Object List and paste all Pathnodes (that shall serve as Teleport Points) into the Object List.
We create a Named Variable for the Teleporter Spawn Point.
We create a Named Variable for the Teleport List.
We create a Modify Object List Node.
We use the Remove from List Input.
As Object Reference we use the Teleporter Spawn Point.
As Object List Variable we use the Teleport List.

We do this to prevent the Bot from teleporting to his Spawning Point where is still standing (if you didn’t do some AI magic).

Step 05: The Bot takes Damage

We create an Attach to Event Node.
As Attachee we set the Teleporter Bot.
We create a Take Damage Event and link it to the Attach to Event Node.
This makes sure that the Teleporter Bot “listens” to the Take Damage Event.
We set the Damage Threshold to 50.
Now the Event will fire when the Teleporter Bot has taken 50 Damage.
We set the Max Trigger Count to 0.
This way the Event can be triggered infinite times.
We set the Re Trigger Delay to 0.1.

Step 06: Choosing a Teleport Point

We create an Access Object List Node.
We use the Random Input.
As Object List we use the Teleport List.
As Output Object we create a new Variable and name it “Teleport Point“.

Now we randomly choose one (1) Pathnode out of our Teleport List which we call Teleport Point.

STEP 07: Preventing a Non-Teleport

We create a Modify Object List Node.
We use the Add to List Input.
As Object Reference we use ALL(!) Pathnodes from the Teleport List.

Do not use the Teleport List itself since it is already missing a Pathnode.

As Object List Variable we use the Teleport List.

Now we have added all possible Pathnodes back to the Teleport List.

We create a Modify Object List Node.
We use the Remove from List Input.
As Object Reference we use the Teleport Point.
As Object List Variable we use the Teleport List.
Now we have removed the Pathnode where our Bot will teleport to from the Teleport List. This way the Bot is not able to teleport itself to this position again and the Lists will always be updated again (every time the Take Damage Event fires).

Last Step: The Teleport

We create a Teleport Node.
As Target we set the Teleporter Bot.
As Destination we set the Teleport Point.

That’s it!
We told the Teleport Bot to teleport to the Teleport Point we defined previously.

Interface 101

With the rise of smartphones, Facebook and free to play, the market for digital games is prospering and broadening. The core gamer is a relatively diminishing target audience and many products are becoming rather a service.

Of course this shift in consumer / costumer is asking for changes in design.
Taking in the changing time frame for playing games additionally, interface and interaction design is becoming more and more crucial.
People spend less time playing games, more often. The first impression becomes momentous, complexity shrinks and understanding the game swiftly thorugh simple and tight designed in- and output is consequential.

So what is important when designing a user interface?
Here, the interface 101.

What is a User Interface? (for the sake of integrity)

Generally a user interface covers what options are available to the user at any given time, how those options are presented, as well as the physical interactions(mouse / keyboard, game pad, etc.).
Additionally the interface helps express the theme of the game (audio-visual style) and communicates the game state.
With video games, the user interface (UI) is divided into two parts: the input, how the player gives commands to the game and the output, how the game communicates the results of those actions to the player.

Questions and guidelines for User Interface design

How easy is the interface to use?
If you already know what you want to do, how fast and easy is it to perform your desired task correctly?

How easy is the interface to learn?
If you are new to the game, how easy is it to figure out what, you are allowed to do and what information is available to you?

In practice, there is often a tradeoff between these two. For example the presence of special hotkeys saves time by making it fast and easy to perform common tasks like saving a file or switching between applications. But if these are the only way to perform that task, it makes the applications difficult to learn for the first time.

Now, what makes a good UI? A good UI does two things.

It does what the user thinks it will do; and
It gives the user feedback so they know what it did.

or

Make it easy to do things correctly; and
Make it hard to do things the wrong way.

Here’s a nifty example by Ian Schreiber: Suppose you have a board game with several kinds of tokens. Maybe you have one set of markers that keeps track of player score, using a scoring track around the edge of the board. Maybe the game board has a map divided into provinces, and players have military units that are placed in the provinces. Maybe there’s also a global market with goods for purchase and sale, and a separate track for each trade good that lists its current price.

It would be easy to get all these different game bits confused. But what if each token was a different size and shape, and each space on the board matched the shape of the token that was used there? All of a sudden, it becomes a lot easier to figure out that the small square tokens must go on the small squares of the scoring track, the star-shaped goods markers go on the star shapes of the trade good price tracks, and so on.

Interface Criteria (Brettmeister, translated real shoddy)

Perceptibilty
The perceptibilty for an interface element is granted, when it is perceived by at least one sense organ.

Feedback
Feedback for an interface element is granted, when every input and every relevant change of state is communicated.

Non-modality
Non-modality for an interface element is granted, when precisely one actions leads to one system’s reaction.
If you can’t go for non-modality, you have to use a workaround.
Either use muscle memory (by contraction, e.g. keyboard keys you have to hold down, like shift) or design the action at the centre of attention.

Unambiguousness
An interface element is unambiguous, when it’s function is clear to 95% of the target audience.

Efficiency
An interface element is efficient, when a task can be performed in the shortest time possible.

Control
The user is in control, when he can work at his own pace. He must understand what his possible actions are and how to perform them.

Habits and References

When it comes to user interface habits play a crucial role. People already know how to play Call of Duty, Starcraft or Farmville. They know how to read the information they need and how to perform the tasks they want to.
Know the habits of your target audience and adapt your design. It is very difficult to erase patterns and habits and build up new ones for the same action is even harder.
And why should the player have a hard time adapting to the interface, when he could have fun with your game instead?
The state of flow must be achieved quick and there is just no time to ask questions.

Deftly powered by Severin Brettmeiser and Ian Schreiber.