[email protected]
Level Design Portfolio
  • HIGHLIGHTS:
    • Camera
    • Collaborations
  • The Long Dark
  • World Design

Telling stories through captivating gameplay.

Warning, this is a construction zone!

Flip through the tabs above to view Open Worlds and other Level Design works I've been crafting since 2014.

Or click the red button
to review my professional work as a Game Design generalist.

Fast Travel to Game Design

Real-Time Cameras in Unreal Editor 4 - Part 2

14/7/2014

5 Comments

 
Reference: Mark Haigh-Hutchinson. 2009. "Real-Time Cameras: A Guide for Game Designers and Developers." Elsevier.

So the camera behaviour in the Code Third Person is as simple as it looks. It is a basic solution that makes decent prototype third person camera, but is not good enough for a finished product. The SpringArmComponent does most of the heavy lifting here, so lets examine it to see if we would be better off extending it or creating our own class that has similar behaviours. Extending the class will mean that we do not need to duplicate code, but rewriting it provides more freedom.


I'll start with a paraphrased quotation from John Nesky's talk "50 Camera Mistakes" that applies to our topic today;
"It is a mistake to push the camera away from an obstacle while the player is trying to swing the camera towards it - conflicts between ideal camera collision avoidance and player intent must favour player intent."

The current SpringArmComponent is not ideal because the player may see something like this if they try to walk up to wall and then look away from it:

Picture
Failure Example for default camera in Code Third Person

Read More
5 Comments

Real-Time Cameras in Unreal Editor 4 - Part 1

12/7/2014

15 Comments

 
Reference: Mark Haigh-Hutchinson. 2009. "Real-Time Cameras: A Guide for Game Designers and Developers." Elsevier.

I have recently begun reading "the textbook" on cameras in gaming, and I am excited to share whatever knowledge I gain from it with you. So, I will be implementing a third-person camera system in Unreal Editor 4 - similar to the one in Journey -  and sharing every step of this process; while attempting to apply my newly learned knowledge in aid of both integrating and communicating these concepts. The main inspiration for this project is a GDC 2014 talk by John Nesky called "50 Camera Mistakes" (see http://gdcvault.com/search.php#&category=free&firstfocus=&keyword=john+nesky&conference_id=).

A starting piece of wisdom from John Nesky, the camera designer for Journey, who says (and I paraphrase)
"A camera is 7 numbers: yaw, pitch, roll, distance from pivot, lateral offset, vertical offset, and field of view."

In this article, I will analyse the existing C++ for the Code Third Person project provided by Epic Games with UE4.

The camera seems to be coded in the "MyCharacter" class. Let's examine the header file to see the variables involved


Variables to keep track of
(this is not working code), referenced by letter index in the following sections
a)
    /** Camera boom positioning the camera behind the character */
    TSubobjectPtr<class USpringArmComponent> CameraBoom;

b)
    /** Follow camera */
    TSubobjectPtr<class UCameraComponent> FollowCamera;

c)
    /** Base turn rate, in deg/sec. Other scaling may affect final turn rate. */
    float BaseTurnRate;

d)
    /** Base look up/down rate, in deg/sec. Other scaling may affect final rate. */
    float BaseLookUpRate;



Now over to the .cpp file to see how these are initialized:

The controller rotation is unhooked
from the character rotation by setting 3 booleans (related to yaw, pitch, and roll) = false.

Picture
Camera Boom

    // Create a camera boom (pulls in towards the player if there is a collision)
    CameraBoom = PCIP.CreateDefaultSubobject<USpringArmComponent>(this, TEXT("CameraBoom"));
    CameraBoom->AttachTo(RootComponent);
    CameraBoom->TargetArmLength = 300.0f; // The camera follows at this distance behind the character    
    CameraBoom->bUseControllerViewRotation = true; // Rotate the arm based on the controller

The camera boom is created first and it follows the player at a set length, rotates based on the controller rotation, and is attacted to the root component of the player. (a) is the pointer used to represent this object.


Read More
15 Comments

Digital Painting: The Blind Mallard and the Hen

5/7/2014

4 Comments

 
Picture
Digital Painting, printed as a puzzle, created by Jim Dodge in Photoshop using a Wacom tablet
4 Comments

How to get random variation using the same seed

28/4/2014

4 Comments

 
You are testing your 2D platformer script that randomly generates a set of platforms based on a song that the player chose. For testing purposes, you use the same song over and over again. But you start to realize that if a player dies in the middle of their song, they will be given the exact same set of platforms if they start again before the song ends. This violates your design because you do not want the player to be able to memorize the pattern of platforms, but you cannot change the seed either. It would make sense in most cases to load a new seed after the player dies, but the background is a visualization of the song that is partially based on the seed number... and your programmer spent weeks coding it to work just right.
Picture
This is Tactical Measure and is not my work. FInd out about what it actually is by clicking here.
Here is an easy solution for anyone who runs into this rare problem, which could have many manifestations other than the example described above. Other solutions include altering the code that selects seeds for your random number generator or using multiple seeds to remove the conditions that require you to keep the same seed. Scripters who do not have access to this code or need a quick fix while they wait for someone else to take a look at it can take advantage of the following method.

1. Use an update loop that will call your GetRandom() function a random* number of times before the first time you need it
2. Try to introduce multiple layers of randomness (i.e. a random distance at a random angle from a random point)**
3. Call GetRandom() a random number of times in between uses and only choose one random solution at a time 
    (and only choose it right before it is needed) instead of determining all of them at the same time using iteration


*the amount of times this function is called cannot be dependant on the seed but could use the current time as input to make a regular or irregular update interval or be procedurally generated by taking the timing of specific player actions as input
**If multiple layers are impossible because you are selecting from three objects that must be ordered randomly for a total of three events, try to introduce a fourth or fifth random object to provide more variation. These new objects can be functionally the same as the already existing objects but should at least be visually different - bigger or smaller and/or a different colour.

l33t section (tl;dr):
WHY? Why would anyone put themselves in this situation in the first place? It could have been accidental, or in some cases it could be desired to maintain the seed in between different play sessions. Procedurally generated levels, as seen in Minecraft, rely on using the same seed during level loading to get the same level composition. The level always reads a set number of characters at the beginning of the seed, but that means all future GetRandom() calls start at the same place each time the level is done loading. As mentioned above, there are better solutions but not everyone is able to implement them.

n00b section (full explanation):
WHAT IS A SEED? It is a pseudorandom string of numbers that are used to generate behaviour that appears random. Having the same seed means having the exact same set of numbers, which leads to the same results even if the designer expects them to be random. Here is an example that explains why the above method works:

THE PROBLEM
Flipping coin simulator with original seed:
010001101001111 = heads, tails, heads, heads, heads, tails, tails, heads, tails, tails, heads, heads, heads, heads

Flipping coin simulation with a new seed:
101010001101001 = tails, heads, tails, heads, tails, heads, heads, heads, tails, tails, heads, tails, heads, heads, tails

Flipping coin simulator which loads the original seed again:
010001101001111 = heads, tails, heads, heads, heads, tails, tails, heads, tails, tails, heads, heads, heads, heads
…now we have a pattern that does not seem random at all. If the player wrote down the order of results from the original seed then they could use that to predict the next "random" coin flip which would definitely make the game feel less random!

THE SOLUTION
We solve this problem by calling our GetRandom() function a random number of times and not printing a result 
The results that are printed now appear random, even with the same seed:

Flipping coin simulator with original seed:
010001101001111 = heads, heads, tails, tails

Flipping coin simulator which loads the original seed again:
010001101001111 = heads, tails, tails, tails


Notice that you are now using a lot more of your seed if you want the same number of printed results. That's OK, seeds are very long and you can pretend that they are of infinite length.

Some people might call this solution "hacky" and it is. I adapted it from a happy coincidence that seemed to be violating my expectations, given that I was quite sure the same seed was being loaded each time I replayed the same level again.        
So, while it is "hacky," it is "happy" too. And if it makes you happy and doesn't ruin anyone else's code then just do it.  
4 Comments
<<Previous
Forward>>

    James Dodge

    Level Designer

    View my profile on LinkedIn

    Categories

    All
    CameraAnalysis
    CameraDevelopment
    GlobalGameJam
    Photoshop
    TombRaider


    Archives

    October 2021
    December 2017
    November 2017
    October 2017
    September 2017
    August 2017
    July 2017
    June 2017
    May 2017
    April 2017
    March 2017
    February 2017
    January 2017
    December 2016
    October 2016
    September 2016
    June 2016
    May 2016
    March 2016
    February 2016
    August 2015
    July 2015
    March 2015
    February 2015
    December 2014
    September 2014
    August 2014
    July 2014
    April 2014
    January 2014
    December 2013
    November 2013
    October 2013
    August 2013


    RSS Feed

Site powered by Weebly. Managed by Bluehost