Module 3 Examples:
Autonomous Behaviors and Tilemap
Link to GitHub
Coverage
Concepts
you will explore and understand include
·
The
lerp function: gradual rotation and chasing
·
Simple
implementation of a finite state machine
·
Randomness
and simple examples in games
·
Sprite
sheets and sprite atlas: for efficiency loading and animation
·
Keyframe
and Sprite animation
Unity specific skills you will be exposed to and begin learning include
·
Sprite
Editor: extracting individual tiles and sprites
·
Animation
Window: defining keyframe animations
·
Animator:
defining animation states, transitions, and, finite state machine
·
TilePallete and Tilemap: for
creating (by painting) levels
- Gradual Rotation and Chasing
- Run
Behavior:
- WASD: move the egg
- UpArrow chase after the egg
- TurnRate: (be careful, slider bar may keep
focus of mouse, click somewhere else to un-focus)
- 0 does not turn
towards egg
- 1 always point at egg
- 0 < rate < 1:
turns gradually from very slow (values close to 0), to very quick
(values close to 1)
- Egg: WASD_Movement:
trivial behavior
- GreenArrowBehavior:
- Public variables
- TurnRate: connects to the
slider
- Target: connects to
the egg
- PointAtPosition(): function
- Pointing the transform.up towards any given position
- Incremental rotation
of transform.up to align with V
- Watch out, if transform.up and V are pointing in perfect
opposite directions, the turning does not work.
- Update()
- Calls PointAtPosition()
- Move in the direction
of transform.up
- In game object movement
consideration
- Easy: Direct control: you
control the position of an object
- Less
Easy:
Indirect control: e.g., you control the velocity of an object
- Obey the
rules of physics: Indirect
control in the world with physics (gravity and potentials for
collisions), you configure the physics to control the object movement
- Follow
your own algorithm:
No control: autonomous (as in this case)
- Learned:
- Gradual rotation: Vector3.lerpUnclamped()
- The PointAtPosition() function
- In-game object
movement considerations
- Autonomous Movement with Randomness:
- Run
Behavior:
- Watch the green arrow
chases the egg, when gets close, egg spawn in a new position in the
pinkish area
- SliderBar changes the pinkish
area size (% of world bound size)
- N-Key: to spawn a new GreenArrow
- H-Key: to hide the egg
- Try: spawn 20 arrows, and
then hide the egg, see a bunch of patrolling arrows?!
- Scene
Setting
- Child: GameManager: empty game object for hanging
the singleton GameManager
- TargetBoundBox (pinkish)
- This box is behind the green arrow (with
larger Z-position values, the larger the Z the more behind).
- Be careful: camera settings only
allows z values of up to 1000: MainCamera.Far
- GreenArrow: chases after the egg
- GameManager:
- Awake():
- Initialize must be
done before GreenArrowBehavior::Start()!
- Has reference
- TargetBoundBox: to be scaled into
the size user specified
- TargetBoundSlider: child of UI-Canvas,
the slider bar
- GetTargetBound: computes the
percentage of a given bound. Note the referencing of
- Camera.main: The Camera
component on Main Camera
- orthographicsSize: half of the height
of the world
- orthographicsSize * aspect: half of
the width of the world
- GreenArrowBehavior:
- ComputeNewTargetPosition: a random target position
within a given bound
- Vector.Distance(): Notice this
function, convenient to use
- Learned:
- Compute the percentage
of a given bound (GameManager::GetTargetBound())
- Random position in a
bound (GreenArrow::ComputeNewTargetPosition())
- Distance between two
game objects: Vector3::Distance()
function
- Game object positions:
z values are important
- Front is smaller z,
behind/far is larger z
- SHOULD NOT change the
z-value of an object during game play
- May cause
“flipping” rotation
- E.g., change the z
value of Target to e.g., 1.0f and observe arrow flips
- SliderBar callback function
- Efficient, only
compute when there are changes
- Edited in the UI
- Finite State Machine
- Run
Behavior:
- Move the arrow with
WASD
- Touch the plane to
trigger fixed behavior
- Finite
State Machine:
Simple state on the Plane. Here is the state
transition definition
- Normally, nothing,
when collision
- enlarge for 120 frames
- rotate CW fast for 80
frames
- rotate CCW for 80
frames
- shrink for 120 frames
- return to resting
- Note: in our context, if an operation
does not need time, it is NOT a state. All states, in the FSM needs more
than one update() cycle.
- Implementation
Keys:
- Draw the state
diagram!!
- Instance variables for
state definition and transition
- Enum for individual
states
- Case statement in
update()
- State service: Each
state is a separate function!
- As states become
complicated, they can be defined as instance of different classes (with
shared base class)
- Organization
- Partial class
(different files implementing the same class)
- Plane.cs and Plane_FSM.cs
- Each contains
separate details!
- Watch out!! Incomplete
(or unintentional) state specification
- Try moving the GreenUp to touch the Plane when it is changing!!
- E.g., when the plane
has grown large, a new collision will trigger a brand new state cycle,
causing the plane to continue to grow larger!
- What should be done to
avoid? Well, when collide, check if current state is indeed in idle.
- Learned:
- Finite State Machine:
important to
- Draw out ALL states,
and
- Define all possible
input and state transitions
- Implementation key:
review the above!
- Relatively
straightforward to define simple repetitive autonomous behaviors!
- C# Partial class: to
help separate source code into different files for organization
- FSM + Randomness
- Run
Behavior:
- All planes cycles
through states without going into resting
- Random enlarge and
rotation periods (turn constants into variables)
- After a while, look
rather random?
- GameManager: hanging off MainCamera
- Creates all the planes
at startup
- Plane:
- Randomness: in the
Start() function, sets the number of frames in scale/rotate states to be
random
- Learned:
- Beauty and simplicity
of random
§ Ease of expansion when well
abstracted
- Keyframe Animation + FSM
- Disclaimer:
Animation is a large and complex topic, we will only cover the
very basic
- Run Behavior:
- I: to trigger Angry
- D: to trigger size
decrease
- Collide plane and
Arrow: to trigger animation state change from Rest-State
- Arrow: Angry (after
current run, when state is Rest)
- Plane: size decrease
(immediately! Interrupting the state flow)
- Define Animation Clips: results of Keyframe animation: Will
associate an “Animation Time Period” with a game object
- Select a GameObject
from the Hierarchy window
- Window->Animation->Animation
(to open the Animation window)
- Create any animation
and you have just associated the defined “Animation Time
Period” with the GameObject
- Create Keyframe
animation:
- Default state: when
“Animation Time Period” is zero
- Click to move the
current-time-line
- Update the
GameObject state (e.g., Transform values in the Scene window
- Click on “Add
Keyframe” (or left-mouse-button->Add Key) to create new
keyframe
- Scrub the
current-time-line to see the defined animation
- Note: at the end of
an animation, the animated attribute will always return back to the initial default
value, even if the final state of the animation is different,
they system will automatically return the attribute to the initial
default: animation is a temporary
deviation from the initial default.
- Organized as States in Animator
where transitions between the States define a Finite State Machine for an
object
- Animator
(controller): controls a collection of States
- Tool for organizing
the States as a Finite State Machine: all animations associated with a
GameObject (e.g., a character)
- State: in the
Animator as simple wraps of existing Animation Clips
- Transitions: detailed
support for Transitions between States
- Solo/Mute check
boxes: for debugging (only testing or mute this transition).
- Parameters: can
define to control the states
- Bool: you must turn
on/off yourself
- Trigger: a Bool, if
you refer to it to cause state transition, system will turn it off
(refer to Begin)
- Examine:
- Animation State:
control over speed
- State transition:
can be simple conditional (or control in script)
- Example: Here is my example
states (Angry FSM):
- Parameter: Begin a Trigger to cause
transition to IncSize
- Parameter: Dec a Bool (to show how it
works), can be a Trigger
- Sharing: It is possible assign the Angry Animator to any
GameObject
- Notice: Arrow and
Plane have the same Angry animator controller
- Exhibits identical
animation from transformation changes
- EXCEPT: with
Animation Event call back, the object needs to have an GreenArrowBehavior
- Scripts:
- ArrowBehavior:
- OnTriggerEnter:
starts Dec animation, and force Plane into DecSize
state
- FromAnimation():
to receive the event from RotateLeft clip
- PlaneBehavior:
- Defining FromAnimation() DOES NOT help: RotateLeft
calls ArrowBehavior::FromAniamtion().
The binding is with ArrowBehaior!
- Angry State
Behaviors:
- RestState:
monitor keyboard to decide who state to transition to
- DecState:
switch off Dec if true (this can be a Trigger)
- Learned:
- Basic concept of
Keyframe Animation and
- When animation is
defined, an object has a Animation-Time-Period attribute
- Animation should be
considered as a deviation-cycle form initial value, will always return
back to the initial value
- Unity how to:
- Animation Window (to
create Animation Clips):
create animation clips for a GameObject
- Animator (to create
Finite State Machine)
- Create empty states
and state transitions
- Create and access
parameters to control state and transitions
- Interrupt current state
and force into another state (CrossFade())
- Can share animator
controller (when only controlling transform), if animation events are
defined trickier to share
- Note:
- When the Arrow is in
the Angry cycle, collision response (Another Angry run) will only
occurred AFTER the end! This is contrast with the Plane’s
response: via CrossFade(), of going into the
response mode immediately.
- It is possible to
create a Finite State Machine with Animator even if there are no
animations involved. I can see both plus (convenient, less coding) and
minus (messy UI workflow, rigid interface). You can decide what you
want to do.
- References:
- Unity manual
pages: (how to use the
Editor)
- Unity API pages:
(involve writing scripts)
- Sprite Animation
- Textures in unity: Sprite Mode
- Single (simple image
of an object)
- Multiple (sprite
sheet, sprite atlas): saves load time and/or define animation
- Organization of an
animated spritesheet
- How to slice: Auto
(optimized) vs Fixed Size (for animation support)
- Unity How to: Slicing Sprite Sheet (Atlas)
- Import Sprite Sheet (Type: Sprite (2D and UI))
- Select the Sprite
- Sprite Mode èMultiple
- Invoke the Sprite Editor, Click on Slice è Apply
- You may have to
install the Sprite Editor using PackageManager
- Search for
“2D Sprite” to install under “Unity Registry”
- Run Behavior:
- I – to toggle
Her idle animation
- Transition to idle
is immediate
- Transition from idle
takes a while about 5x idle cycles
- HeroKnight
animation is RICH! (don’t know all of them)
- E: kills the hero
- LMB: attach
- There are much
more!!
- An Example:
- Three spritesheets: HerPlayer, HimPlayer, and TinyTown
- Slice into
individual sprites
- HerPlayer
and HimPlayer by FixedSize
(grid-cell count)
- TinyTown
with Auto detection in size
- Drag individual
sprite to create GameObject with no animation: drag from TinyTown Tilemap_48 and Tilemap_49 objects are
dragged from individual Sprite elements (elements 48 and 49 from TinyTown)
- Drag entire spritesheet to create GameObject with an Animator:
e.g., HerPlayer. (Rename the create Animator
in the Asset window), repeat for HimPlayer
- Share the created
Animator: Unfortunately, this cannot be done! Association with Sprite is
static
- Try it, replace the HimPlayer’s Animator control with Her_Entire_Sheet!
- Painfully created Her_Idle animation clip + state
- Lesson learned: this
stuff is challenging!
- NOT done: if we want
to support interactions, we must define colliders for each of the
sprites! Here is a short tutorial on what you can
do (create associated keyframes for the collider): https://www.youtube.com/watch?v=ls1WxW1zXNc
- Recommendation:
- Learned: some basic Unity how to
- Work with sprite
sheet: slice into individual tiles with Sprite Editor (remember to set
the mode to Multiple)
- Drag to create
GameObject with default sprite animation
- Create simple sprite
animation and support animation state transition
- Good animation is
tedious, and more importantly, challenging!
- References:
- Tile Palettes and Tilemap
- What:
- Create a level by painting it: three main topics
- Tile sprite sheet:
this is like the color, choice of colors to be used in a painting
- Tilemap:
this is like the canvas (target to be painted on)
- TilePalette:
this is like the color to be painted on a canvas, in this case, a
“color” is a
selected tile from the tile sprite sheet
- Run Behavior:
- WASD and Space to
control the HeroKnight
- Tile Sprite sheet:
- Turn a tile sprite
sheet into individual sprites (usually with Auto Slicing)
- Remember: the
size of each sprite element:
- Controlled/refined by Texture Pixels Per Unit
- TilePalette:
- Tilemap:
- Example paint operation:
- Tile sprite sheet sutup: Drag in your tile spritesheet,
make Multiple, slice into individual tiles
- Open TilePalette, create the new palette using your tile
sprite sheet
- Create a Tilemap object (will create with a Grid and one Map
child): name the child Background
- Refine tile pixel
size (this is what I do): paint a tile in the grid, adjust the tile
sprite sheet Pixels Per Unit (or grid size) until each tile covers
exactly one grid.
- Now, paint the
Background
- When done, get ready
to paint platforms (with colliders)
- Firstly, disable
Background (make it inactive)
- Create a new tilemap (RMB over Grid-> 2D Object->Tilemap->Rectangular)
- New name the new map
to Platforms
- In the TilePlatte: select Platforms as target to work on
- Now, paint the
platforms
- Repeat for MovableTiles map
- Collision support:
- Idea: separate
collide-able tiles in separate layers of maps. In our case, the Platform
map.
- After drawing, can
switch on collision for individual maps by adding TilemapCollider2D
component: (Add Component->Tilemap->Tilemap Collider 2D)
- Note: all defined
tiles in the layer will have Collider2D defined and can detect collision
- For more
accurate/smooth collision support: on MovableTiles
- CompositeCollider2D:
can add a CompositeCollider2D to the Tilemap’s
map to smooth out issues
involved with too many colliders
- Here is a short
discussion on the issue: https://www.youtube.com/watch?v=Z0O59mWbq1E
- Note:
- A RigidBody2D
component will be automatically added to the map, you probably want to
change the type to Kinematic.
- The
TileMapCollider2D::Used by Composite switch!

- Note:
- Remember: Collider2D
size can be adjusted (e.g., to be larger or smaller than the actual
object)
- For collision to
occur, one of the parties must
have RigidBody2D defined (everything learned about collision in
Unity applies here). So, if hero wants to walk over the tiles, the Hero
should have RigidBody2D (and at least one Collider2D) defined. Recall:
- Dynamic: will be
move according to Physics (try setting Gravity Scale to 0)
- Kinematic: only for
collision but cannot response (no movement after colliding)
- Player/TileMap Layering: https://www.youtube.com/watch?v=rVBzTKvoStk
- Control:
Renderer->Additional Setting: Order in layers
- Layers: 2: HeroKnight, 1: Platform, 0:Background
- Learned: again mostly Unity tools
- TilePlatte:
how to create, how to paint on different maps
- Tilemap:
separate maps for separate characteristics, including colliders
- Collisions: probably
always use Composite Colliders for platformers
- Credits: