992 lines
46 KiB
TeX
992 lines
46 KiB
TeX
%! TeX program = lualatex
|
|
\documentclass[a4paper,11pt]{article}
|
|
% packages
|
|
\usepackage{fontspec}
|
|
\setmainfont{EB Garamond}
|
|
% for tironian et fallback
|
|
% % \directlua{luaotfload.add_fallback
|
|
% % ("emojifallback",
|
|
% % {"Noto Serif:mode=harf"}
|
|
% % )}
|
|
% % \setmainfont{EB Garamond}[RawFeature={fallback=emojifallback}]
|
|
|
|
\setmonofont[Scale=MatchLowercase]{Deja Vu Sans Mono}
|
|
\usepackage[a4paper,left=2cm,right=2cm,top=\dimexpr15mm+1.5\baselineskip,bottom=2cm]{geometry}
|
|
\setlength{\parindent}{0pt}
|
|
|
|
\usepackage{fancyhdr} % Headers and footers
|
|
\fancyhead[R]{\normalfont \leftmark}
|
|
\fancyhead[L]{}
|
|
\pagestyle{fancy}
|
|
|
|
\usepackage{multicol}
|
|
\usepackage{microtype} % Slightly tweak font spacing for aesthetics
|
|
\usepackage[english]{babel} % Language hyphenation and typographical rules
|
|
\usepackage[final, colorlinks = true, urlcolor = blue, linkcolor = black]{hyperref}
|
|
% \urlstyle{tt}
|
|
\usepackage{changepage} % adjust margins on the fly
|
|
|
|
\usepackage{minted}
|
|
\usemintedstyle{algol_nu}
|
|
\usepackage{xcolor}
|
|
|
|
\usepackage{pgfplots}
|
|
\pgfplotsset{width=\textwidth,compat=1.9}
|
|
|
|
\usepackage{caption}
|
|
\newenvironment{code}{\captionsetup{type=listing}}{}
|
|
|
|
\usepackage[yyyymmdd]{datetime}
|
|
\renewcommand{\dateseparator}{-}
|
|
|
|
\usepackage{titlesec}
|
|
|
|
\begin{document}
|
|
\begin{titlepage}
|
|
\begin{center}
|
|
\hrule
|
|
\vspace*{0.6cm}
|
|
\huge \textbf{CT3536}
|
|
\vspace*{0.6cm}
|
|
\hrule
|
|
\LARGE
|
|
\vspace{0.5cm}
|
|
GAMES PROGRAMMING
|
|
\vspace{0.5cm}
|
|
\hrule
|
|
|
|
\vfill
|
|
\includegraphics[width=0.8\textwidth]{images/unity-logo.png}
|
|
\vfill
|
|
|
|
\Large
|
|
\vspace{0.5cm}
|
|
\hrule
|
|
\vspace{0.5cm}
|
|
\textbf{Andreas Ó hAoḋa}
|
|
% \vspace{0.5cm}
|
|
% \hrule
|
|
% \vspace{0.5cm}
|
|
|
|
\normalsize
|
|
University of Galway
|
|
|
|
\today
|
|
|
|
\vspace{0.5cm}
|
|
\hrule
|
|
\end{center}
|
|
\end{titlepage}
|
|
|
|
\pagenumbering{roman}
|
|
\newpage
|
|
\tableofcontents
|
|
\newpage
|
|
\setcounter{page}{1}
|
|
\pagenumbering{arabic}
|
|
|
|
\section{Introduction}
|
|
\subsection{Lecturer Contact Information}
|
|
\begin{itemize}
|
|
\item Dr. Sam Redfern (\href{mailto://sam.redfern@nuigalway.ie}{\texttt{sam.redfern@nuigalway.ie}})
|
|
\item Discord server: \url{https://discord.gg/nqD5JN95WT}
|
|
\end{itemize}
|
|
|
|
\subsection{Assessment}
|
|
There will be 1-3 person projects which will begin from approximately Week 5, which will be worth 30\% of the course marks.
|
|
10\% of the overall mark will be from the six graded lab assignments, and the remaining 60\% will be from the final exam.
|
|
\\\\
|
|
The game project will have a demo in the final week, and will be submitted as a document containing images, discussions,
|
|
code, etc.
|
|
Marks are awarded for:
|
|
\begin{itemize}
|
|
\item Overall complexity.
|
|
\item Code architecture \& neatness.
|
|
\item Game design/elegance \& UX.
|
|
\item Discussion.
|
|
\item Graphics (if self-created).
|
|
\item Audio (if self-control).
|
|
\item Group size will also be taken into account.
|
|
\end{itemize}
|
|
|
|
You should start to consider your ideas by Week 5.
|
|
Discuss the idea with Dr. Redfern \& the lab tutors as they can advise on scope \& difficulty.
|
|
Simple 3D games are no harder to make than 2D games.
|
|
``Snake'' games are not allowd!'
|
|
|
|
|
|
\subsection{Game Engines}
|
|
\textbf{Game Engines} provide a powerful set of integrated sub-systems geared towards making games (and other high-performance
|
|
\emph{realtime} media) including:
|
|
\begin{itemize}
|
|
\item Graphics Rendering (3D, 2D, terrain).
|
|
\item Physics.
|
|
\item Networking.
|
|
\item Special Effects.
|
|
\item 3D Audio.
|
|
\item User Input.
|
|
\end{itemize}
|
|
|
|
\subsection{Unity3D Game Engine}
|
|
The Unity3D game engine is a closed-source games engine that is well designed \& elegant to use ``2\textsuperscript{nd}
|
|
generation'' game engine.
|
|
It has excellent GUI/HUD editing \& animation system, and is powerful \& very popular.
|
|
It has a hugely successful Asset Store.
|
|
Its core language is C\#, which is very similar to Java.
|
|
Unity3D deploys to iOS, Android, Web (HTML5/WebGL), Windows, Mac OSX, Linux, Switch, \& more.
|
|
\begin{figure}[H]
|
|
\centering
|
|
\includegraphics[width=0.9\textwidth]{./images/unity_ide.png}
|
|
\caption{The Unity IDE}
|
|
\end{figure}
|
|
|
|
The \textbf{Project Window} displays your library of assets that are available to use in your project.
|
|
When you import assets into your project, they appear here.
|
|
\begin{figure}[H]
|
|
\centering
|
|
\includegraphics[width=0.9\textwidth]{./images/project_window.png}
|
|
\caption{The Project Window (Assets)}
|
|
\end{figure}
|
|
|
|
The \textbf{Scene View} allows you to visually navigate \& edit your scene without running your game.
|
|
The scene view can be a 3D or 2D perspective.
|
|
\begin{figure}[H]
|
|
\centering
|
|
\includegraphics[width=0.9\textwidth]{./images/scene_view.png}
|
|
\caption{The Scene View}
|
|
\end{figure}
|
|
|
|
The \textbf{Hierarchy Window} is a hierarchical nested text representation of every ``\textbf{game object}'' in the scene.
|
|
\begin{figure}[H]
|
|
\centering
|
|
\includegraphics[width=0.9\textwidth]{./images/hierarchy_window.png}
|
|
\caption{The Hierarchy (Scene Graph) Window}
|
|
\end{figure}
|
|
|
|
The \textbf{Inspector Window} allows you to view \& edit all the properties of the currently selected game object.
|
|
\begin{figure}[H]
|
|
\centering
|
|
\includegraphics[width=0.9\textwidth]{./images/inspector_window.png}
|
|
\caption{The Inspector Window}
|
|
\end{figure}
|
|
|
|
The \textbf{toolbar} provides access to the most essential working features.
|
|
On the left it contains the basic tools for manipulating the scene view \& the objects within it.
|
|
In the centre are the play \& pause controls.
|
|
The buttons to the right give you access to your Unity Cloud Services \& your Unity Account, followed by a \textbf{layer}
|
|
visibility menu, and finally the editor layout menu which provides some alternate layouts for the editor windows, and allows
|
|
you to save your own custom layouts.
|
|
\begin{figure}[H]
|
|
\centering
|
|
\includegraphics[width=0.9\textwidth]{./images/toolbar.png}
|
|
\caption{The Toolbar}
|
|
\end{figure}
|
|
|
|
\subsubsection{The Game Loop}
|
|
At their core, games operate a \textbf{game loop}, although game engines somewhat hide this from you.
|
|
It operates at 60fps (or more), deal with inputs received asynchronously via events (e.g. keyboard or network data) or
|
|
polled for right now, process game objects such as move the physics simulation (if any) forwards or move objects by
|
|
physics simulation or direct control, redraw, wait (maybe) or run at maximum obtainable speed (maybe) but don't block the
|
|
main thread.
|
|
|
|
\subsubsection{The Unity API}
|
|
Luckily, everything is not just drag-n-drop.
|
|
We can write C\# code to make the game work.
|
|
Much of our code will involve the manipulation of the Unity API classes from the \verb|UnityEngine| \& \verb|UnityEngine.UI|
|
|
namespaces.
|
|
Of course, we can also do anything else supported by the core C\#/.NET library such as file handling, networking, collection
|
|
classes, etc.
|
|
|
|
\subsubsection{Component-Based Architecture}
|
|
2\textsuperscript{nd} generation game engines such as Unity3D use \textbf{Component-Based Architecture (CBA)}, which suits
|
|
game logic very well.
|
|
It's different to classic OOP as it's based on the \textbf{Composition} rather than \textbf{Inheritance} principle.
|
|
Every \textbf{entity} consists of one or more \textbf{components} which add additional behaviour or functionality.
|
|
The behaviour of an entity can even be changed at runtime by adding or removing components.
|
|
This eliminates the ambiguity problems of inheritance class-hierarchies that are difficult to understand, maintain, \&
|
|
extend.
|
|
Each component is essentially a separate software object but all attached to some higher-level \verb|GameObject| which
|
|
typically equates to an actual onscreen character, enemy, bullet, effect, vehicle, etc.
|
|
\\\\
|
|
Using composition rather than inheritance can be somewhat of a mantra, and not correct in all cases.
|
|
Code re-use is often (incorrectly) considered to be the main principle of inheritance.
|
|
In fact, designing hierarchical taxonomies of classes is the most appropriate reason to use inheritance.
|
|
Composition is better when you would otherwise get tangles up in the murky world of multiple-inheritance.
|
|
Composition is excellent for code re-use.
|
|
|
|
\section{Lab Session 1}
|
|
Import a model of Mars (and a \verb|texture.jpg| for it).
|
|
Create a simple demo which rotates Mars using the built-in physics engine.
|
|
We could also take direct control of the Mars object and rotate it ourselves, a little bit on each frame, but here we're
|
|
letting the physics engine do the work.
|
|
No need to submit anything for this lab session as it's not graded.
|
|
|
|
\begin{code}
|
|
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{csharp}
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
public class GameManagerScript : MonoBehaviour {
|
|
public GameObject marsObject;
|
|
// Use this for initialization
|
|
void Start () {
|
|
Camera.main.transform.position = new Vector3 (0f, 0f, -100f);
|
|
Camera.main.transform.LookAt (marsObject.transform);
|
|
// use the physics engine to rotate Mars
|
|
// before this can run, you need to manually add a rigid body with 0 angular velocity and no gravity in the UI
|
|
marsObject.GetComponent<Rigidbody>().AddTorque (new Vector3(0f,20f,0f));
|
|
}
|
|
}
|
|
\end{minted}
|
|
\caption{Lab 1 Code}
|
|
\end{code}
|
|
|
|
\begin{code}
|
|
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{csharp}
|
|
public class GameManager : MonoBehaviour {
|
|
// inspector settings
|
|
public GameObject marsObject;
|
|
void Start() {
|
|
marsObject.transform.position = new Vector3(0,0,0);
|
|
Camera.main.transform.position = new Vector3(0,0,100);
|
|
Camera.main.transform.LookAt(marsObject.transform);
|
|
}
|
|
void Update() {
|
|
// programmatically rotate Mars each frame:
|
|
marsObject.transform.Rotate(new Vector3(0,10*Time.deltaTime,0));
|
|
}
|
|
}
|
|
\end{minted}
|
|
\caption{Alternative Lab 1 Code}
|
|
\end{code}
|
|
|
|
\section{Key Concepts \& Classes}
|
|
\subsection{\texttt{MonoBehaviour}}
|
|
\textbf{\texttt{MonoBehaviour}} is the base class from which Unity C\# scripts normally derive.
|
|
This hooks the class into the Game Loop so that it automatically receives calls to specific methods at specific times.
|
|
It also provides various other useful Unity methods that are called by the game engine in specific situations, including but
|
|
not limited to:
|
|
\begin{multicols}{3}
|
|
\begin{itemize}
|
|
\item \verb|Start()|
|
|
\item \verb|OnDestroy()|
|
|
\item \verb|Awake()|
|
|
\item \verb|Update()|
|
|
\item \verb|FixedUpdate()|
|
|
\item \verb|LateUpdate()|
|
|
\item \verb|OnDisable()|
|
|
\item \verb|OnEnabled()|
|
|
\item \verb|OnBecameInvisible()|
|
|
\item \verb|OnBecameVisible()|
|
|
\item \verb|OnCollisionEnter()|
|
|
\item \verb|OnCollisionExit()|
|
|
\item \verb|OnCollisionStay()|
|
|
\item \verb|OnTriggerEnter()|
|
|
\item \verb|OnTriggerExit()|
|
|
\item \verb|OnTriggerStay()|
|
|
\item \verb|OnMouseEnter()|
|
|
\item \verb|OnMouseExit()|
|
|
\item \verb|OnMouseDown()|
|
|
\end{itemize}
|
|
\end{multicols}
|
|
|
|
In games, it is often useful to be able to execute code at programmer-controlled intervals (perhaps much less often than every
|
|
frame), or at some specified time in the future.
|
|
In Unity \verb|MonoBehaviour|, the methods \verb|Invoke()| \& \verb|StartCoroutine()| relate to this.
|
|
We will elaborate on these later.
|
|
\verb|MonoBehaviour| also provides several important data members such as:
|
|
\begin{multicols}{2}
|
|
\begin{itemize}
|
|
\item \verb|enabled| (Boolean).
|
|
\item \verb|gameObject| (GameObject).
|
|
\item \verb|transform| (Transform).
|
|
\item \verb|name| (String) (name of \verb|gameObject|).
|
|
\end{itemize}
|
|
\end{multicols}
|
|
|
|
Finally, there are some more methods provided which are useful for manipulating \verb|GameObject|s and their components:
|
|
\begin{multicols}{3}
|
|
\begin{itemize}
|
|
\item \verb|SendMessage()|
|
|
\item \verb|BroadcastMessage()|
|
|
\item \verb|SendMessageUpwards()|
|
|
\item \verb|GetComponent()|
|
|
\item \verb|GetComponentInChildren()|
|
|
\item \verb|GetComponentInParent()|
|
|
\item \verb|GetComponents()|
|
|
\item \verb|GetComponentsInChildren()|
|
|
\item \verb|GetComponentsInParent()|
|
|
\item \verb|GetInstanceID()|
|
|
\end{itemize}
|
|
\end{multicols}
|
|
|
|
Recall that \verb|GameObject|s can contain many independent scripts (components), each inheriting from \verb|MonoBehaviour|.
|
|
Each script/component is an instance of a class.
|
|
A \verb|GameObject| is therefore composed of multiple software objects.
|
|
This is called a ``component-based system'' and is often seen as a superior approach to object-oriented class hierarchies --
|
|
better for code re-use \& isolation of functionality.
|
|
\\\\
|
|
References:
|
|
\begin{itemize}
|
|
\item \url{https://docs.unity3d.com/ScriptReference/MonoBehaviour.html}
|
|
\item \url{https://docs.unity3d.com/Manual/ExecutionOrder.html}
|
|
\end{itemize}
|
|
|
|
\subsection{\texttt{GameObject}s in Unity}
|
|
\verb|GameObject| is the base class for all entities that exist in a Unity scene.
|
|
As discussed above, each \verb|GameObject| has a collection of components attached, and each component is an independent class
|
|
object inheriting from \verb|MonoBehaviour|.
|
|
The \verb|GameObject| is a special class that \emph{all} entities in the game are derived from and which you won't have to
|
|
attach as a component.
|
|
Some useful data members of \verb|GameObject| include:
|
|
\begin{itemize}
|
|
\item \verb|activeInHierarchy| (Boolean).
|
|
\item \verb|transform| (Transform).
|
|
\item \verb|tag| (Tag type as defined in the editor).
|
|
\end{itemize}
|
|
|
|
Some useful methods of \verb|GameObject| include:
|
|
\begin{itemize}
|
|
\item \verb|AddComponet()|.
|
|
\item \verb|SendMessage()| etc. (same as the method in \verb|MonoBehaviour| classes).
|
|
\item \verb|GetComponent()| etc. (same as the method in \verb|MonoBehaviour| classes).
|
|
\item \verb|SetActive()|.
|
|
\end{itemize}
|
|
|
|
Some useful static methods of \verb|GameObject| include:
|
|
\begin{itemize}
|
|
\item \verb|Find()|.
|
|
\item \verb|Destroy()|.
|
|
\item \verb|Instantiate()|.
|
|
\end{itemize}
|
|
|
|
\subsubsection{Getting References to \texttt{GameObject}s at Runtime}
|
|
For a specific \verb|GameObject|:
|
|
\begin{itemize}
|
|
\item Have it referenced as a public member of the script that needs it, and associated at design-time in the Inspector.
|
|
(This is what we did in the first lab for the Mars object, a reference to which was needed by the
|
|
\verb|GameManager| class).
|
|
\item Use \verb|GameObject.Find("<name>")| to find it by name. This is somewhat inefficient, so don't do this every frame.
|
|
\item Use \verb|GameObject.FindGameObjectsWithTag("<tag>")| to find all game objects with a specified tag. This returns an array
|
|
of \verb|GameObject|s.
|
|
\end{itemize}
|
|
|
|
\subsubsection{Prefabs}
|
|
When you have created a \verb|GameObject| in the hierarchy (at design time), added components to it, and set their various
|
|
public values, you can drag the \verb|GameObject| into the Assets Window to make a \textbf{prefab}, essentially a template of
|
|
that exact object with its settings.
|
|
\\\\
|
|
This approach means that you can make \verb|MonoBehaviour| scripts for monsters, guns, etc. and then make a separate prefab
|
|
with different settings for each actual type of monster or gun you need in your game.
|
|
Prefabs are therefore useful for creating \& editing your game's data.
|
|
|
|
\subsubsection{Runtime Instantiation \& Destruction of \texttt{GameObject}s}
|
|
So far, we have created all of the \verb|GameObject|s that we need in the hierarchy at design-time (i.e., before starting the
|
|
game).
|
|
Often, we need to instantiate \& destroy some or all of our game objects at runtime, e.g. bullets, enemies, explosions, etc.
|
|
Assuming that we have a \textbf{prefab} in our Assets, and that it is located in a directory called \verb|Resources| we can
|
|
do the following.
|
|
Here, \verb|prefabName| is the name of our asset prefab (as a String):
|
|
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{csharp}
|
|
GameObject go = Instantiate(Resources.Load(prefabName));
|
|
|
|
// later on, assuming we still have a reference to the object:
|
|
GameObject.Destroy(go);
|
|
// or
|
|
GameObject.DestroyImmediate(go);
|
|
\end{minted}
|
|
|
|
Alternatively, you can instantiate an object by supplying an existing instantiated object as a template.
|
|
E.g., in Lab 1 our \verb|GameManagerScript| class had a \verb|GameObject| member called \verb|marsObject|, which is
|
|
instantiated in the Inspector at design time, so you could use this to make another Mars:
|
|
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{csharp}
|
|
GameObject otherMarsObject = Instantiate(marsObject);
|
|
\end{minted}
|
|
|
|
Another alternative, perhaps the easiest of all, is to have a public reference to a \verb|GameObject| and associate this
|
|
with a prefab in the assets by dragging in the Inspector.
|
|
Then you can use \verb|Instantiate()| on that to instantiate a copy of the prefab into the scene at runtime.
|
|
|
|
\subsubsection{Cameras}
|
|
In computer graphics, \textbf{Cameras} are software objects that define a viewpoint in terms of position, orientation,
|
|
field-of-view/zoom, etc. within the ``virtual world''.
|
|
Mathematical projections are used to calculate how the 3D world should be displayed on the 2D camera surface.
|
|
In Unity, Cameras are \verb|GameObject|s which have a \verb|Camera| component.
|
|
Since they're \verb|GameObject|s, you can add other components, manipulate their \verb|Transform|, etc. just like any other
|
|
\verb|GameObject|.
|
|
The \verb|Transform| component provides methods that are very useful to Cameras (as well as other non-Camera objects) such as
|
|
\verb|LookAt()|.
|
|
\\\\
|
|
\verb|Camera.main| is a static reference to the ``main'' Camera whose view is being displayed to the screen.
|
|
This Camera should have the tag \verb|MainCamera|.
|
|
Other Cameras can be used at the same time, e.g. to render to texture (e.g. mirrors, top-down minimaps) or to render to parts
|
|
of the screen (e.g., split-screen multiplayer).
|
|
You might also have multiple cameras in a scene and switch the active one during the game.
|
|
\\\\
|
|
The \verb|Camera| class provides methods for converting co-ordinates between Screen-space, Viewport-space, \& World-space
|
|
(more on these later).
|
|
\begin{itemize}
|
|
\item \verb|CalculateFrustumCorners()|: Given viewport coordinates, calculates the view space vectors pointing to the
|
|
four frustum corners at the specified camera depth.
|
|
\item \verb|ScreenPointToRay()|: Returns a ray going from camera through a screen point.
|
|
\item \verb|ScreenToViewportPoint()|: Transforms position from screen space into viewport space.
|
|
\item \verb|ScreenToWorldPoint()|: Transforms position from screen space into world space.
|
|
\item \verb|ViewportPointToRay()|: Returns a ray going from camera through a viewport point.
|
|
\item \verb|ViewportToScreenPointTransforms()|: position from viewport space into screen space.
|
|
\item \verb|ViewportToWorldPointTransforms()|: position from viewport space into world space.
|
|
\item \verb|WorldToScreenPointTransforms()|: position from world space into screen space.
|
|
\item \verb|WorldToViewportPointTransforms()|: position from world space into viewport space.
|
|
\end{itemize}
|
|
References:
|
|
\begin{itemize}
|
|
\item \url{https://docs.unity3d.com/ScriptReference/Camera.html}
|
|
\item \url{https://docs.unity3d.com/Manual/class-Camera.html}
|
|
\end{itemize}
|
|
|
|
\subsubsection{Transforms}
|
|
A \textbf{Transform} defines the position, orientation, \& scale of an object.
|
|
Child objects have their own Transforms which are interpreted as a nested co-ordinate system, i.e. a modification of the
|
|
parent's.
|
|
This is a very powerful concept and is fundamental to the concept of a SceneGraph/hierarchy.
|
|
In Unity, the \verb|Transform| class has the following key members:
|
|
\begin{itemize}
|
|
\item \verb|position| (Vector3)
|
|
\item \verb|localPosition| (Vector3)
|
|
\item \verb|rotation| (Quaternion)
|
|
\item \verb|localRotation| (Quaternion)
|
|
\item \verb|lossyScale| (Vector3)
|
|
\item \verb|localScale| (Vector3)
|
|
\item \verb|parent| (Transform)
|
|
\item \verb|right|: positive direction on the local x axis (Vector3, normalised).
|
|
\item \verb|up|: positive direction on the local y axis (Vector3, normalised).
|
|
\item \verb|forward|: positive direction on the local z axis (Vector3, normalised).
|
|
\item \verb|gameObject| (GameObject)
|
|
\end{itemize}
|
|
|
|
Methods of the \verb|Transform| class include:
|
|
\begin{itemize}
|
|
\item \verb|Rotate()|: Uses ``Euler angles'' ($x$, $y$, $z$) all in degrees.
|
|
\item \verb|Translate()|
|
|
\item \verb|TransformPoint()|: Transforms position from local space to world space.
|
|
\item \verb|InverseTransformPoint()|: Transforms position from world space to local space.
|
|
\item \verb|LookAt()|: (Vector3 point) turns so that the forward direction (i.e., positive $z$ axis) faces the specified
|
|
position.
|
|
\item \verb|RotateAround()|: (Vector3 point, Vector3 axis, float degrees).
|
|
\item \verb|SetParent()|
|
|
\end{itemize}
|
|
|
|
Reference: \url{https://docs.unity3d.com/ScriptReference/Transform.html}
|
|
|
|
\subsubsection{Skyboxes}
|
|
\textbf{Skyboxes} are rendered around the whole scene in the background in order to give the impression of complex scenery at
|
|
the horizon.
|
|
To implement a Skybox, create a Skybox Material in your Assets.
|
|
Then, add it to the scene by using the Window {\rightarrow} Rendering {\rightarrow} Lighting menu item and specifying your Skybox
|
|
material as the Skybox on the Scene tab.
|
|
\\\\
|
|
Reference: \url{https://docs.unity3d.com/Manual/class-Skybox.html}
|
|
|
|
\subsubsection{Keyboard Input}
|
|
We will revisit more details about Keyboard/Mouse/Joystick/Touchscreen input later, but for now we will just say that
|
|
\verb|Input.GetKey()| returns true as long as a key is held down.
|
|
E.g., this could be written in an \verb|Update()| method:
|
|
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{csharp}
|
|
if (Input.GetKey(KeyCode.LeftArrow)) {
|
|
RotateCameraAroundMarsSomehow();
|
|
}
|
|
\end{minted}
|
|
|
|
References:
|
|
\begin{itemize}
|
|
\item \url{https://docs.unity3d.com/ScriptReference/Input.html}
|
|
\item \url{https://docs.unity3d.com/ScriptReference/Input.GetKey.html}
|
|
\end{itemize}
|
|
|
|
\subsubsection{Static Member Variables}
|
|
By declaring a member variable \textbf{\texttt{static}}, you're creating a single copy of the variable which is owned by the
|
|
class rather than by any specific instance object.
|
|
Each instance does not have its own unique copy, they share one.
|
|
A common use case for static member variables is if you want to retain a collection of all instances of the class for quick
|
|
recall.
|
|
|
|
\subsubsection{Static Member Functions}
|
|
By declaring a method \textbf{\texttt{static}}, you're creating a method that is called on the class itself, not an instance of
|
|
it.
|
|
This is very useful since you don not need to have a reference to an instantiated object from the class in order to call it.
|
|
For obvious reasons, a static method only has access to static member variables of the class, while a non-static method
|
|
(which is called on an actual instantiated object) has in addition, access to its own copies of non-static member variables.
|
|
|
|
\subsubsection{The Singleton Pattern}
|
|
A common design approach in Unity is to create single-instance \textbf{singleton} classes for various ``management'' roles,
|
|
e.g. \verb|GameManager|, \verb|GUIManager|, \verb|AudioManager|, \verb|SaveGameManager|, etc.
|
|
The typical approach is to create a \verb|GameObject| in the hierarchy which holds the one-and-only instance of these
|
|
components that will exist at run-time.
|
|
One habit is to attach these to the Main Camera.
|
|
Using statics can make the methods of these management singletons very easy \& clean to access, e.g. consider the ``instance''
|
|
static member variable used here; it gives direct access to data attached to the instantiated singleton object, from anywhere
|
|
in the game, via \verb|GameManager.instance|
|
|
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{csharp}
|
|
public class GameManager : MonoBehaviour {
|
|
public static GameManager instance;
|
|
|
|
void Start () {
|
|
instance = this;
|
|
}
|
|
}
|
|
\end{minted}
|
|
|
|
\subsection{Lab 2: Fear \& Dread}
|
|
In our first lab, we attached a \verb|Rigidbody| to the Mars object, applied angular velocity to it using \verb|AddTorque|,
|
|
and let the game engine control the movement of it.
|
|
The other way of moving game objectsis to directly manipulate their \verb|Transform| using code (this was our approach in
|
|
second year Java).
|
|
In Lab 2, we'll be making Mars' two moons (Phobos \& Deimos) orbit around it through direct manipulations of their Transforms.
|
|
Your code for doing this would typically be written in the \verb|Update()| method of some script (it could either be a
|
|
script attached to the \verb|GameObject| itself, or perhaps a script attached to a singleton ``manager'' object).
|
|
It is important to consider the fact that \verb|Update()| is probably not happening at fixed time intervals.
|
|
You can multiply all movement code by \verb|Time.deltaTime|.
|
|
|
|
\section{Co-ordinate Systems}
|
|
The most important co-ordinate system distinction in Unity is between the \textbf{Global} (or World) co-ordinate system
|
|
and the \textbf{Local} co-ordinate system of each \verb|GameObject|.
|
|
Hence, to rotate the camera round the $y$-axis \emph{as perceived by the camera} (i.e., the $y$-axis of its Local Co-ordinate
|
|
system) we can use:
|
|
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{csharp}
|
|
camera.transform.RotateAround(Vector3.zero, camera.transform.up, 50f * Time.deltaTime);
|
|
\end{minted}
|
|
|
|
Or, to rotate it around the Global $y$-axis:
|
|
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{csharp}
|
|
camera.transform.RotateAroudn(Vector3.zero, Vector3.up, 50f * Time.deltaTime);
|
|
\end{minted}
|
|
|
|
The first \verb|Vector3| argument defines the world point around which to rotate.
|
|
\\\\
|
|
Some methods of the \verb|Transform| class for translating from the local space of a transform to global space and
|
|
vice-versa include:
|
|
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{csharp}
|
|
// local space to global
|
|
public Vector3 TransformPoint(Vector3 position);
|
|
public Vector3 TransformDirection(Vector3 direction);
|
|
public Vector3 TransformVector(Vector3 vector);
|
|
// global to local
|
|
public Vector3 InverseTransformPoint(Vector3 position);
|
|
public Vector3 InverseTransformDirection(Vector3 direction);
|
|
public Vector3 InverseTransformVector(Vector3 vector);
|
|
\end{minted}
|
|
|
|
\subsection{Examples}
|
|
To find the world-coordinate of a point which is 10 units ``in front of'' a spaceship (in terms of its
|
|
own direction facing, and assuming this code is located inside a script attached to the spaceship):
|
|
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{csharp}
|
|
Vector3 pt = transform.TransformPoint(new Vector3(0f, 0f, 10f));
|
|
// another way of doing the same would be:
|
|
Vector3 pt = transform.position + 10f * transform.forward;
|
|
\end{minted}
|
|
|
|
To accelerate a spaceship forwards (assuming it has a \verb|Rigidbody| and we're using physics, and we're doing this in a
|
|
\verb|FixedUpdate()| method on one of its scripts):
|
|
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{csharp}
|
|
Rigidbody rigid = GetComponent<Rigidbody>();
|
|
rigid.AddForce(transform.forward * 200f * Time.fixedDeltaTime);
|
|
\end{minted}
|
|
|
|
To get the direction \& distance between two \verb|GameObject|s:
|
|
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{csharp}
|
|
GameObject go1, go2; // it's assumed that these are not nulls!
|
|
|
|
Vector3 difference, direction;
|
|
difference = go2.transform.position - go1.transform.position;
|
|
direction = difference.normalized;
|
|
float distance = difference.magnitude;
|
|
\end{minted}
|
|
|
|
Iterating nested objects using \verb|foreach (Transoform t in Transform)|:
|
|
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{csharp}
|
|
public class Area : MonoBehaviour {
|
|
// inspector settings
|
|
public Transform innerWallsGroup;
|
|
public Vector3 wallsPos;
|
|
|
|
private List<GameObject> innerWalls = new List<GameObject>();
|
|
private bool[] wallIsRaised = null;
|
|
|
|
void Start() {
|
|
foreach (Transform t in innerWallsGroup) {
|
|
innerWalls.Add(t.gameObject);
|
|
}
|
|
|
|
wallIsRaised = new bool[innerWalls.Count];
|
|
|
|
for (int i = 0; i < innerWalls.Coutn; i++) {
|
|
innerWalls[i].transform.position = wallsPos;
|
|
innerWalls[i].SetActive(false);
|
|
wallIsRaised[i] = false;
|
|
}
|
|
}
|
|
}
|
|
\end{minted}
|
|
|
|
\subsection{Screen Space, Viewport Space, \& World Space}
|
|
Reference: \url{https://docs.unity3d.com/ScriptReference/Camera.html}
|
|
\\\\
|
|
The \verb|Camera| class provides methods for translating between three different ``spaces'', i.e. different ways of
|
|
mapping the positions of things.
|
|
\\\\
|
|
Each of these is stored as a \verb|Vector3| (which is a \verb|struct|).
|
|
A \textbf{screen space} point is defined in pixels.
|
|
The bottom-left of the screen is $(0,0)$; the right-top is $(\texttt{Camera.pixelWidth}, \texttt{Camera.pixelHeight})$ .
|
|
If you're using a \verb|Vector3| rather than a \verb|Vector2|, then the $z$ position is in \textbf{world units} in front
|
|
of the Camera, rather than in screen pixels.
|
|
See also the \verb|Screen| class: \url{https://docs.unity3d.com/ScriptReference/Screen.html}
|
|
\\\\
|
|
A \textbf{viewport space} point is normalised and relative to the Camera.
|
|
The bottom-left of the Camera is $(0,0)$; the top-right is $(1,1)$.
|
|
Again, the $z$ position (if any) is in world units in front of the Camera.
|
|
E.g., this is appropriate for positioning GUI elements independent of actual pixel resolution of the screen.
|
|
\\\\
|
|
A \textbf{world space} is defined in global co-ordinates (for example, \verb|transform.position|.
|
|
E.g., this is appropriate for \verb|GameObject|s in the game world.
|
|
\verb|GameObject|s which are nested in the Hierarchy have both \verb|transform.position| \&
|
|
\verb|transform.localPosition|; \verb|transform.rotation| \& \verb|transform.localRotation|, etc.
|
|
|
|
\subsubsection{Typical Space-Translation Operations}
|
|
Find where the mouse is in world co-ordinates to see if a \verb|GameObject| is under the mouse:
|
|
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{csharp}
|
|
// note that Input.mousePosition gives a Vector4 where the $z$ component is 0
|
|
// the screen is 2D of course; the $z$ component of the Vector3 supplied to ScreenToWorldPoint is a world-coordinate distance into the world.
|
|
Vector3 mousePosInWorld = Camera.main.ScreenToWorldPoint(Input.mousePosition);
|
|
\end{minted}
|
|
|
|
Find a \verb|GameObject| in screen-pixel coordinates to position a GUI item such as a healthbar above it:
|
|
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{csharp}
|
|
GameObject targ; // assumed not to be null
|
|
Vector3 screenPos = Camera.main.WorldToScreenPoint(targ.transform.position);
|
|
\end{minted}
|
|
|
|
\begin{code}
|
|
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{csharp}
|
|
// Here, we find the 3D position that's just above the enemy's head, and convert this to a vieweort position when viewed through the player's camera.
|
|
// Finally, this viewport position is converted to a pixel position as required by the GUI sprite.
|
|
public class MonsterManager : MonoBehaviour {
|
|
void Update () {
|
|
if (GameManager.gameState==GameStates.Playing) {
|
|
if (numMonstersAlive==1) {
|
|
// put the final enemy indicator (GUI object) above the final monster
|
|
GameObject go = GUIManager.instance.finalEnemyIndicator;
|
|
Monster m = allActiveMonsters[0];
|
|
Bounds b = m.mycollider.bounds;
|
|
Vector3 pos = new Vector3(b.center.x, b.max.y + 1f, b.center.z);
|
|
Vector3 viewPos = Player.myPlayer.fpsCamera.WorldToViewportPoint(pos);
|
|
if (viewPos.x<0f || viewPos.x>1f || viewPos.y<0f || viewPos.y>1f || viewPos.z<0f)
|
|
go.SetActive(false);
|
|
else {
|
|
go.SetActive(true);
|
|
go.transform.position = new Vector2(viewPos.x*Screen.width, viewPos.y*Screen.height);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
\end{minted}
|
|
\caption{The ``Final Enemy Indicator'' in DemonPit}
|
|
\end{code}
|
|
|
|
\section{Object Interactions with Colliders \& Triggers}
|
|
Objects which have \verb|Collider|s \& \verb|Rigidbody|s will physically respond to impacts (i.e., they'll bounce off each
|
|
other).
|
|
Objects which have Triggers but not Colliders will \textbf{not} physically interact.
|
|
\\\\
|
|
Related to the physics engine are two sets of \verb|MonoBheaviour| methods which are called automatically when objects
|
|
with Colliders or Triggers interact:
|
|
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{csharp}
|
|
// these happen for Collider-to-Collider collisions
|
|
OnCollisionEnter(Collision coll);
|
|
OnCollisionExit(Collision coll);
|
|
OnCollisionStay(Collision coll);
|
|
|
|
// these happen for Trigger-to-Trigger collisions
|
|
OnTriggerEnter(Collider coll);
|
|
OnTriggerExit(Collider coll);
|
|
OnTriggerStay(Collider coll);
|
|
\end{minted}
|
|
|
|
The \verb|Collision| argument provides information on the game object that has collided with us, as well as additional
|
|
information about the collision itself (e.g., the speed of impact).
|
|
The \verb|Collider| argument is simply a reference to the Trigger component that interact with ours.
|
|
\verb|OnCollision| refers to an actual physics interaction between two Colliders (e.g. \verb|BoxCollider| or
|
|
\verb|SphereCollider| components).
|
|
\verb|OnTrigger| is the same thing except here the Collider has its Trigger flag set to \verb|true|.
|
|
Triggers enable game-logic interaction when you don't want physical collision responses.
|
|
\\\\
|
|
Reference: \url{https://docs.unity3d.com/ScriptReference/Collider.OnCollisionEnter.html}
|
|
|
|
\subsection{Lab 3: Asteroid Assault on Mars \& its Moons}
|
|
This lab builds on the work from the last two weeks; we already have Mars rotating, with Phobos \& Deimos orbiting around
|
|
it, and keyboard control of the camera.
|
|
This week, we will be instantiating asteroids at runtime, and setting them moving (using physics).
|
|
Asteroid which collide with anything will be destroyed, as will be asteroids which pass offscreen.
|
|
|
|
\subsection{Triggers (Sensors)}
|
|
\textbf{Triggers} are invisible components that are activated (triggered) when a character or other object passes inside
|
|
them.
|
|
They are a very common \& useful mechanism in games.
|
|
In Unity, Trigger is made by using a Collider with its Trigger property checked.
|
|
|
|
\subsection{Coroutines}
|
|
\textbf{Coroutines} are special functions that can pause their execution.
|
|
They're very useful for delayed execution, making things happen in steps (with a defined delay between each step), or for
|
|
waiting for something else to complete before executing.
|
|
Coroutines are a very useful feature provided by the \verb|MonoBehaviour| class.
|
|
Their usefulness is because the code related to something occurring in the game is kept all in one place, even if that
|
|
something takes a long time to occur, or occurs in stages.
|
|
Coroutines are not threads, as they run in the same thread as the main Unity process, but they are much simpler to use than
|
|
threads.
|
|
In any case, most of the Unity SDK is not thread-safe.
|
|
Coroutines are started with a call to \verb|StartCoroutine()|, and they must have the \verb|IEnumerator| return type.
|
|
Technically, the way that these work is that Unity maintains a list of active Coroutines and on each game loop wakes up
|
|
any that are now due to wake up.
|
|
\\\\
|
|
When the coroutine is activated, it well execute until the next \verb|yield| statement and then pause until it is
|
|
resumed.
|
|
Various values can be supplied with the \verb|yield| statement, e.g. \verb|yield return null| simply waits until the next
|
|
frame.
|
|
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{csharp}
|
|
private void Start() {
|
|
Debug.Log("Start method");
|
|
StartCoroutine(TestCoroutine());
|
|
Debug.Log("Start method ends");
|
|
}
|
|
|
|
// this coroutine has a loop that runs as along as the calling object is active
|
|
private IEnumerator TestCoroutine() {
|
|
Debug.Log("TestCoroutine");
|
|
while(true)
|
|
{
|
|
Debug.Log("Here");
|
|
yield return null;
|
|
Debug.Log("There");
|
|
}
|
|
}
|
|
|
|
private void Update() {
|
|
Debug.Log("Update");
|
|
}
|
|
|
|
// Output should be:
|
|
/*
|
|
Start method
|
|
TestCoroutine
|
|
Here
|
|
Start method ends
|
|
Update
|
|
There
|
|
Here
|
|
Update
|
|
There
|
|
Here
|
|
*/
|
|
\end{minted}
|
|
|
|
Some of the most useful Coroutine \verb|yield| values include:
|
|
\begin{itemize}
|
|
\item \verb|yield return null;| -- the coroutine is continued the next time that it is eligible, normally on the
|
|
next frame.
|
|
\item \verb|yield return new WaitForSeconds(3f);| -- causes the coroutine to pause for a specified time period (three
|
|
seconds in this example). Be aware of the garbage implications of this.
|
|
\item \verb|yield return StartCoroutine(OtherCoroutine());| -- waits until the other coroutine has run to completion
|
|
before the yielder is resumed.
|
|
\item \verb|yield break;| -- stops the coroutine and exits (i.e., this is the equivalent of a \verb|return| statement
|
|
in a normal method).
|
|
\end{itemize}
|
|
|
|
Be aware of changes during the \verb|yield| time.
|
|
You always have to be careful with asynchronous programming: something important may have changed during the ``down time''
|
|
of a coroutine.
|
|
\\\\
|
|
Reference: \url{https://unitygem.wordpress.com/coroutines/}
|
|
|
|
\subsubsection{\texttt{Invoke} \& \texttt{InvokeRepeating}}
|
|
\verb|Invoke| \& \verb|InvokeRepeating| provide a simpler way of running a complete method some time in the future, either
|
|
as a one-off or a repeating call.
|
|
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{csharp}
|
|
public class ExampleScript : MonoBehaviour
|
|
{
|
|
// Launches a projectile after 2 seconds
|
|
|
|
Rigidbody projectile;
|
|
|
|
void Start() {
|
|
Invoke("LaunchProjectile", 2f);
|
|
}
|
|
|
|
void LaunchProjectile()
|
|
{
|
|
Rigidbody instance = Instantiate(projectile);
|
|
instance.velocity = Random.insideUnitSphere * 5f;
|
|
}
|
|
}
|
|
\end{minted}
|
|
|
|
To invoke repeatedly:
|
|
\begin{minted}[texcl, mathescape, linenos, breaklines, frame=single]{csharp}
|
|
InvokeRepeating("LaunchProjectile", 2.0f, 0.3f);
|
|
\end{minted}
|
|
|
|
\subsection{Layers}
|
|
\textbf{Layers} allow you to group game objects into categories.
|
|
They are can be found at Edit {\rightarrow} Project Settings {\rightarrow} Tags and Layers.
|
|
You can then decide which layers interact with each other in the physics simulation.
|
|
You can also use an object's Layer to decide what happens when it collides with another object.
|
|
You can also apply raycasts (see later) on specific objects only, e.g. to determine whether a monster can see a player,
|
|
you would cast a ray between the position of the monster's head and the player's head, but only let the raycast consider
|
|
walls/floors, i.e. it would ignore any creatures in the way.
|
|
\\\\
|
|
Don't confuse Layers with Sorting Layers: the latter is for deciding the draw order of Sprites.
|
|
\\\\
|
|
Reference: \url{https://docs.unity3d.com/560/Documentation/Manual/Layers.html}
|
|
|
|
\section{Physics}
|
|
\subsection{Rigidbody}
|
|
References:
|
|
\begin{itemize}
|
|
\item \url{https://docs.unity3d.com/Manual/RigidbodiesOverview.html}
|
|
\item \url{https://docs.unity3d.com/ScriptReference/Rigidbody.html}
|
|
\end{itemize}
|
|
|
|
A \verb|Rigidbody| is the main component that enables phyiscal behaviour for a \verb|GameObject|;
|
|
it puts the \verb|GameObject| under the control of the physics engine.
|
|
The object will respond to gravity if it has a \verb|Rigidbody| component attached, (provided that the \verb|useGravity|
|
|
field is not set to \verb|false|.
|
|
If one or more Collider components are added, the \verb|GameObject| is affected by incoming collisions (from other Colliders)
|
|
according to its shape, mass, momentum, linear, \& angular velocities, as well as Physics Materials which defined
|
|
bounciness \& friction.
|
|
\\\\
|
|
Since a \verb|Rigidbody| component takes over the movement of the \verb|GameObject| that it's attached to, you normally
|
|
shouldn't try to move it from a script by changing the Transform properties such as position \& rotation; instead you
|
|
should apply forces to push the \verb|GameObject| and let the physics engine calculate the results.
|
|
When a \verb|Rigidbody| is moving slower than a defined minimum linear or rotational speed, the physics engine assumes
|
|
that it has come to a halt.
|
|
When this happens, the \verb|GameObject| does not move again until it receives a collision or force, and so it is set to
|
|
``\textbf{sleeping}'' mode.
|
|
This optimisation means that no processor time is spent updating the \verb|Rigidbody| until the next time that is
|
|
``awoken'', i.e. set in motion again.
|
|
Sleeping can also be useful as it removes small ``jitters'' that may happen due to inaccuracies in the physics simulation.
|
|
In your scripts which apply physics forces to Rigidbodies, use the \verb|FixedUpdate()| method rather than the
|
|
\verb|Update()| method, since \verb|FixedUpdate()| is synced with the physics simulation updates.
|
|
\\\\
|
|
\verb|Rigidbody| properties:
|
|
\begin{itemize}
|
|
\item \mintinline{csharp}{public float drag}
|
|
\item \mintinline{csharp}{public float angularDrag}
|
|
\item \mintinline{csharp}{public float mass}
|
|
\item \mintinline{csharp}{public Vector3 velocity}
|
|
\item \mintinline{csharp}{public Vector3 angularVelocity}
|
|
\item \mintinline{csharp}{public Vector3 centerOfMass} (offset from Transform centre).
|
|
\end{itemize}
|
|
|
|
\verb|Rigidbody| methods
|
|
\begin{itemize}
|
|
\item \mintinline{csharp}{public void AddForce(Vector3 force)} (\verb|force| is a vector in world co-ordinates
|
|
applied through the centre of mass of the \verb|Rigidbody|.
|
|
\item \mintinline{csharp}{public void AddRelativeForce(Vector3 force)} (\verb|force| is a vector in local
|
|
co-ordinates).
|
|
\item \mintinline{csharp}{public void AddForceAtPosition(Vector3 force, Vector3 position)}
|
|
|
|
\end{itemize}
|
|
|
|
\subsection{Colliders}
|
|
Reference: \url{https://docs.unity3d.com/Manual/CollidersOverview.html}
|
|
\\\\
|
|
\textbf{Collider} components define the shape of an object for the purposes of physical collisions.
|
|
Colliders are invisible at runtime, and do not need to be the exact same shape as the object's mesh; a rough
|
|
approximation is of the shape is often more efficient, and is indistinguishable in gameplay.
|
|
The simplest (\& least processor-intensive) colliders are the \textbf{primitive} collider types, which all
|
|
inherit from the superclass \verb|Collider|.
|
|
In 3D, these are the \verb|BoxCollider|, \verb|SphereCollider|, \& \verb|CapsuleCollider|.
|
|
In 2D, these are \verb|BoxCollider2D| \& \verb|CircleCollider2D|.
|
|
Any number of these can be added to a single object to create composite colliders to reasonably approximate a 3D
|
|
model.
|
|
\\\\
|
|
If you need more accuracy (at increased processor cost), use \textbf{\texttt{MeshCollider}} which accurately
|
|
matches the 3D graphical model (the polygonal mesh).
|
|
A \verb|MeshCollider| will be unable to collide with another \verb|MeshCollider| unless you mark it as
|
|
\verb|Convex| in the inspector.
|
|
This will generate the collider shape as a ``convex hull'' which is like the original mesh with concavities
|
|
filled in.
|
|
The general rule is to use mesh colliders for static scene geometry (walls, ground, etc.) and to approximate the
|
|
shape of moving objects using composite primitive colliders.
|
|
\begin{figure}[H]
|
|
\centering
|
|
\includegraphics[width=0.7\textwidth]{./images/MeshCollider.png}
|
|
\caption{\texttt{MeshCollider}: Convex Hull vs Non-Convex}
|
|
\end{figure}
|
|
|
|
\begin{figure}[H]
|
|
\centering
|
|
\includegraphics[width=0.7\textwidth]{./images/CompositeCollider.png}
|
|
\caption{Composite Collider}
|
|
\end{figure}
|
|
|
|
\subsubsection{Collisions}
|
|
Reference: \url{https://docs.unity3d.com/ScriptReference/Collision.html}
|
|
\\\\
|
|
Any object with a Collider receives messages from the physics engine when it collides with other Colliders.
|
|
Any script on the object may choose to respond by implementing the following methods:
|
|
\begin{itemize}
|
|
\item \mintinline{csharp}{void OnCollisionEnter(Collision collision)} (the \verb|Collision| object contains
|
|
information about contact points, impact velocity, etc.)
|
|
\item \mintinline{csharp}{void OnCollisionExit(Collision collision)}
|
|
\item \mintinline{csharp}{void OnCollisionStay(Collision collision)}
|
|
\end{itemize}
|
|
|
|
The \verb|OnCollision| methods receive objects of type \verb|Collision|, which contain useful information such as:
|
|
\begin{itemize}
|
|
\item \verb|collider|: The \verb|Collider| object that was hit.
|
|
\item \verb|contacts|: The contact point(s) generated by the physics engine as an array of
|
|
\verb|ContactPoint| structs.
|
|
\item \verb|gameObject|: The \verb|GameObject| whose collider was collided with.
|
|
\item \verb|relativeVelocity|: The relative linear velocity of the two colliding objects as a \verb|Vector3|.
|
|
\item \verb|rigidbody|: The \verb|Rigidbody| that was hit; this is \verb|null| if the object had a collider
|
|
but no \verb|Rigidbody|.
|
|
\end{itemize}
|
|
|
|
\subsubsection{\texttt{ContactPoint} Struct}
|
|
Reference: \url{https://docs.unity3d.com/ScriptReference/ContactPoint.html}
|
|
\\\\
|
|
\verb|Collision| objects contain arrays of \verb|ContactPoint| structs in their \verb|.contacts| member variable.
|
|
The member data of the \verb|ContactPoint| struct is as follows:
|
|
\begin{itemize}
|
|
\item \verb|point|: The point of contact in \verb|Vector3| world co-ordinates.
|
|
\item \verb|normal|: The surface normal at the contact point in \verb|Vector3|.
|
|
\item \verb|otherCollider|: The other \verb|Collider| in contact at the point.
|
|
\item \verb|thisCollider|: The first collider in contact at the point (useful if a \verb|GameObject| has
|
|
more than one \verb|Collider| and we need to know which one).
|
|
\end{itemize}
|
|
|
|
These fields allow us to operate on the object that we hit and to create accurate special effects etc. at the
|
|
point of contact, rotated according to the surface normal at that point.
|
|
|
|
\subsubsection{\texttt{PhysicMaterial}}
|
|
When Colliders interact, their surfaces need to simulate the properties of the material that they are supposed to
|
|
represent.
|
|
For example, a sheet of ice will be slippery while a rubber ball will offer a lot of friction and be very bouncy.
|
|
Although the shape of colliders is not deformed during Collisions (hence the term ``rigid body physics''), their
|
|
friction \& bounce can be configured using \verb|PhysicMaterial|s.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
\end{document}
|