The Gang of Four describe the command pattern as "Encapsulate a request as an object, thereby letting users parameterise clients with different request, queue or log requests, and supports undoable operations."Another way to describe it as a thingified method call, which means to wrap a method call in a function.
The command pattern is very similar to callbacks, first-class functions, function pointers and partially applied functions. The Gang of Four also describle commands as an object oriented replacement for callbacks.
An example use for the command pattern is configurable/remappable input, what this means the user can change what actions a button/bumper/trigger can do. Usually specific actions are mapped to specific input like so:
void InputHandling()
{
if (Input.GetButtonDown(BUTTON_X))
{
Reload();
}
else if(Input.GetButtonDown(BUTTON_Y))
{
SwapWeapons();
}
else if(Input.GetButtonDown(BUTTON_B))
{
Melee();
}
else if(Input.GetButtonDown(BUTTON_A))
{
Jump();
}
}
We could set up preset functions that contain different variations of the type of input and corresponding actions. However, that might not be flexible enough and so allowing the players to swap out actions is a better alternative, which is where commands come in. We can use pointers for each button and when the player wants to change that buttons input we simply point to a new command object.
Example Code
Command Class
class Command
{
public:
virtual ~Command() { };
virtual void Execute() = 0;
};
Example Command Classes
class JumpCommand : public Command
{
public:
virtual void Execute() { Jump(); }
};
class MeleeCommand : public Command
{
public:
virtual void Execute() { Melee(); }
};
class SwapWeaponsCommand : public Command
{
public:
virtual void Execute() { SwapWeapons(); }
};
Input Handler
class InputHandler
{
public:
void HandleInput();
// Methods to bind commands
private:
Command* buttonA;
Command* buttonB;
Command* buttonX;
Command* buttonY;
};
void HandleInput()
{
if (Input.GetButtonDown(BUTTON_X))
{
buttonX->Execute();
}
else if(Input.GetButtonDown(BUTTON_Y))
{
buttonY->Execute();
}
else if(Input.GetButtonDown(BUTTON_B))
{
buttonB->Execute();
}
else if(Input.GetButtonDown(BUTTON_A))
{
buttonA->Execute();
}
}
However, this assumes that whatever functions that are wrapped in the execute functions as part of the command class, can find the player to do whatever action is required. This isn't always the case and so we can modify the code so that we pass in a reference to an object and then call a specific function of that object. This means we now make these changes to the code:
class Command
{
public:
virtual ~Command() { }
virtual void Execute(Actor& actor) = 0;
};
class SwapWeaponsCommand : public Command
{
public:
virtual void Execute(Actor& actor)
{
actor.SwapWeapons();
}
};
Command* HandleInput()
{
if (Input.GetButtonDown(BUTTON_X))
{
return buttonX;
}
if(Input.GetButtonDown(BUTTON_Y))
{
return buttonY;
}
if(Input.GetButtonDown(BUTTON_B))
{
return buttonB;
}
if(Input.GetButtonDown(BUTTON_A))
{
return buttonA;
}
return NULL:
}
You will notice that we now return the commands, this is because we don't know the actor to execute functions from and so once we know the command, we can pass in the reference to the actor, to the command. Like so:
Command* command = HandleInput();
if (command)
{
command->execute(actor);
}
This also means we can actually control any actor within the game, as we can just swap out which actor is passed in to the execute function.
Thursday, January 24, 2019
Number Systems: Introductions
Number systems allow us to count various things with a lot more ease than before. The most common one is Decimal or Base 10, this is most likely because humans generally have 10 fingers, 5 on both hands.
Base 10
Base 10 uses 10 symbols: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
This combined with places allows us to use large numbers such as 231.
If we split 231 down we get:
- 2 x 100s
- 3 x 10s
- 1 x 1s
It is interesting to note that each place increases a multiple of 10, but also can be explained exponentially so:
- 1 = x10^0
- 10 = x10^1
- 100 = x10^2
- 1000 = x10^3
Base 2 - Binary
Base 2 uses 2 symbols: 0, 1
These usually represents on and off or true and false.
Instead of each place being represented by x10^n, base 2 places are represented by x2^n.
So the first 8 places of binary are:
- 1 = x2^0
- 2 = x2^1
- 4 = x2^2
- 8 = x2^3
- 16 = x2^4
- 32 = x2^5
- 64 = x2^6
- 128 = x2^7
We can now convert 231 base 10 into base 2
128 64 32 16 8 4 2 1
---------------------------
1 1 1 0 0 1 1 1
So in binary 231 is represented as 11100111 and we can check this by adding each place that has a value of 1: 128 + 64 + 32 + 4 + 2 + 1 = 231
Base 16 - Hexadecimal
Base 16 uses 16 symbols: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A (10), B(11), C(12), D(13), E(14), F (15)
As with base 2, base 16 places are represented by something different to x10^n. The places are represented by x16^n. So the first 4 places of base 16 are:
- 1 = x16^0
- 16 = x16^1
- 256 = x16^2
- 4096 = x16^3
We can now figure out how to write 231 in base 16
4096 256 16 1
-------------------
E 7
So in base 16, 231 can be represented as E7 and we can check this by adding the each places total together.
E or 14 x 16 = 224
7 x 1 = 7.
224 + 7 = 231
Sample Problem: C++ - Order of Execution of Constructors & Destructors Between a Parent & Child Class
You have a parent class that has a constructor and a non-virtual destructor, as well as a child class that inherits from the parent class. What is the execution order for the constructors and destructors?
- Parent constructor is called
- Child constructor is called
- Child destructor is called
- Parent destructor is called
Source Code
Source.cpp
#include "Parent.h"
#include "Child.h"
#include <iostream>
int main()
{
Child* testChild = new Child();
testChild->~Child();
int endInput;
std::cin >> endInput;
return 0;
}
Parent.h
#pragma once
#include <iostream>
class Parent
{
public:
Parent();
~Parent();
};
Parent.cpp
#include "Parent.h"
Parent::Parent()
{
std::cout << "Parent Constructor Called" << std::endl;
}
Parent::~Parent()
{
std::cout << "Parent Destructor Called" << std::endl;
}
Child.h
#pragma once
#include "Parent.h"
#include <iostream>
class Child : Parent
{
public:
Child();
~Child();
};
Child.cpp
#include "Child.h"
Child::Child()
{
std::cout << "Child Constructor Called" << std::endl;
}
Child::~Child()
{
std::cout << "Child Destructor Called" << std::endl;
}
Console Output
Wednesday, January 23, 2019
Matrices Pt 2
If you haven't read part one, it might be a good idea as it goes over the basics of matrices.
Multiplication
You can think of calculating the matrices multiplication by thinking in terms of vectors and dot products, For example, you have matrix A and matrix B, both are a 2x2 matrix, you want to multiply the two together to get matrix C. The top left value of matrix C is equal to the dot product of the first row of matrix A and the first column of matrix B. Then to get the top right value of matrix C, you do the same but using the second column of matrix B. The bottom two values of matrix C can be calculated in the same before but using the second row of matrix A.
Of course this scales upwards so if you have two 4x4 matrices, the working out is still the same as before. Multiplication doesn't require both matrices to have the same dimensions, although it does require the number of columns in matrix A to be the same as the number of rows in matrix B.
It should be noted that matrix multiplication is note commutative, however, it is both associative and distributive over addition. Also if a matrix is multiplied by the identity matrix then the matrix stays the same. You can also calculate the identity matrix by multiplying a matrix by its inverse.
Matrix Multiplication Properties
Multiplication
You can think of calculating the matrices multiplication by thinking in terms of vectors and dot products, For example, you have matrix A and matrix B, both are a 2x2 matrix, you want to multiply the two together to get matrix C. The top left value of matrix C is equal to the dot product of the first row of matrix A and the first column of matrix B. Then to get the top right value of matrix C, you do the same but using the second column of matrix B. The bottom two values of matrix C can be calculated in the same before but using the second row of matrix A.
Of course this scales upwards so if you have two 4x4 matrices, the working out is still the same as before. Multiplication doesn't require both matrices to have the same dimensions, although it does require the number of columns in matrix A to be the same as the number of rows in matrix B.
It should be noted that matrix multiplication is note commutative, however, it is both associative and distributive over addition. Also if a matrix is multiplied by the identity matrix then the matrix stays the same. You can also calculate the identity matrix by multiplying a matrix by its inverse.
Matrix Multiplication Properties
- A * B ≠ B * A - This means that it isn't communative
- A * (B + C) = A * B + A * C - This means that it is distributive
- A * (B * C) = C * (A * B) - This means that it is associative
- A = A * I
- I = A * A^-1
Matrix Multiplication Dimension
The dimensions of the end matrix of a matrix multiplication are the number of rows in the first matrix and the number of columns in the second matrix. So if we had a 3x2 matrix and a 2x5 matrix, the dimension of the final matrix is 3x5.
The dimensions of the end matrix of a matrix multiplication are the number of rows in the first matrix and the number of columns in the second matrix. So if we had a 3x2 matrix and a 2x5 matrix, the dimension of the final matrix is 3x5.
Transpose
When transposing a matrix, you convert each row of the matrix to its corresponding column, so the first row becomes the first column,etc. Transposing is denoted by a T.
Transforming 3D Vectors by Matrices
So you need to transform a vector by a specific matrix, well the first thing is to convert the vector into a matrix. There are two ways to do this, row major and column major.
For example, Vector A = {1, 2, 3} can either look like:
Row Major Version |
Column Major Version |
Depending on the kind of matrix you are multiplying by it makes sense to convert to that type of major. So if a matrix is intended to be multiplied by a row major matrix then convert it to that type of major. However, if that is not possible you can transpose the multiplaction matrix to multiply with the opposite major type.
x^1 = x * a + y * b + z * c
y^1 = x * d + y * e + z * f
z^1 = x * g + y * h + z * i
x^1 = a * x + b * y + c *z
y^1 = d * x + e * y + f * z
z^1 = g * x + h * y + i * z
So it actually doesn't matter which major is used but it is best to be consistent with which one is used throughout a game.
All images were created using https://www.codecogs.com/latex/eqneditor.php
Matrices Pt 1
A matrix is made up of a grid of real numbers, the grid can be r rows and c columns. If a matrix has 3 rows and 2 columns it is referred to as a 3x2 matrix. A matrix is typically expressed by a capital letter. These can be used to make changes or transform a vector or position in video games. There are two ways to declare a matrix
Method 1
Method 2
Identity Matrix
The identity matrix is a matrix with equal number of rows and columns to another matrix, as well as this each component of the identity matrix is 0, except for a diagonal of 1s that start in the top left and works its way down.
Addition & Subtraction
In order to add or subtract two matrices they have to have the same dimensions or in other words, the amount of rows must match and the amount of columns must match. When adding two matrices we simply add each corresponding component together.
Matrix Equations
Matrix equations are equations where a matrix is being represented by a letter, similarly to how you might have the equation 5x = 15, which when solved gives x = 3.
An example of this is below, where we have to find A.
Scalar Multiplication
This is the same as scalar multiplication of vectors as each component of the matrix is multiplied by the scalar value.
Method 1
Method 2
Identity Matrix
The identity matrix is a matrix with equal number of rows and columns to another matrix, as well as this each component of the identity matrix is 0, except for a diagonal of 1s that start in the top left and works its way down.
Addition & Subtraction
In order to add or subtract two matrices they have to have the same dimensions or in other words, the amount of rows must match and the amount of columns must match. When adding two matrices we simply add each corresponding component together.
The same goes for subtraction as we subtract each corresponding component from one another.
Matrix Equations
Matrix equations are equations where a matrix is being represented by a letter, similarly to how you might have the equation 5x = 15, which when solved gives x = 3.
An example of this is below, where we have to find A.
The first step is to get A on its own.
Then we carry out the matrix subtraction to give us the value of A.
Scalar Multiplication
This is the same as scalar multiplication of vectors as each component of the matrix is multiplied by the scalar value.
Zero Matrices
A zero matrix is a matrix which has ever element as a value of 0. A zero matrix is represented with a 0 like below and the dimensions are subscripted to that 0.
When you add a zero matrix to any matrix you get the original matrix, whereas if you add the opposite of a matrix to the original matrix you get the zero matrix. If you multiply a matrix by zero you get a zero matrix that has the same dimensions as the original.
All images were created using https://www.codecogs.com/latex/eqneditor.php
Linear Interpolation
Linear interpolation or lerp is used to calculate a linear value that is between two values, i.e. lerp could return a value of 0.5 as it's between 0 and 1, which were the two original values given to the function. So in this instance the value is 50% between the two original values.
Lerping can be applied to a wide range of values and not just real world numbers, such as colour, vectors and quaternions. These example are made up of multiple dimensions such as colour usually being made up of 3/4 floats that represent R, G, B and A), so no matter how many dimensions make up something, lerping can still be applied using the generic formula:
Lerp(_a, _b, _f) = (1 - _f) * _a + (_f * _b)
Where _a and _b are the two points that are being interpolated between and _f is within the fractional range of [0, 1] in terms of _a and _b. It is also important to note that this formula is not frame rate independant and so by using deltaTime * _f will achieve that.
An example use of lerping is moving a platform from point A to point B, where each tick the platform's position is now a percentage between point A and point B using lerp. There are other variations on lerp such as slerp which is spherical lerping, that provide similar effects to lerping.
Lerping can be applied to a wide range of values and not just real world numbers, such as colour, vectors and quaternions. These example are made up of multiple dimensions such as colour usually being made up of 3/4 floats that represent R, G, B and A), so no matter how many dimensions make up something, lerping can still be applied using the generic formula:
Lerp(_a, _b, _f) = (1 - _f) * _a + (_f * _b)
Where _a and _b are the two points that are being interpolated between and _f is within the fractional range of [0, 1] in terms of _a and _b. It is also important to note that this formula is not frame rate independant and so by using deltaTime * _f will achieve that.
An example use of lerping is moving a platform from point A to point B, where each tick the platform's position is now a percentage between point A and point B using lerp. There are other variations on lerp such as slerp which is spherical lerping, that provide similar effects to lerping.
Friday, January 18, 2019
Unreal Engine - Blueprints Essential Concepts
What is a Blueprint?
A blueprint is a container for content as it can hold various components, scripts and data. It doesn't always need a script as it can be a data only blueprint. This means the designers can modify the data but not modify the behaviour of the blueprint.
Blueprints are a compiled object oriented visual scripting language and ties into the pre-exisiting UE4 framework class hierarchy. It is also completely embedded within UE4 and works by stringing together connections and nodes. You can also see the adjustments fairly quickly after the blueprint has been compiled.
There are two types of blueprints:
A blueprint is a container for content as it can hold various components, scripts and data. It doesn't always need a script as it can be a data only blueprint. This means the designers can modify the data but not modify the behaviour of the blueprint.
Blueprints are a compiled object oriented visual scripting language and ties into the pre-exisiting UE4 framework class hierarchy. It is also completely embedded within UE4 and works by stringing together connections and nodes. You can also see the adjustments fairly quickly after the blueprint has been compiled.
There are two types of blueprints:
- Level blueprint which is one per level and only affects that level
- Class blueprint which can have multiple instances within a level and works in all levels
Blueprints are built on C++ and in fact when using blueprints you are using C++ as they can be even openned and edited in C++. This means both play nicely together.
Creating Blueprints
When creating a new blueprint, you can pick from a parent class which gives the new class some inherited functionality (hence why its object oriented). Most cases the parent class will be an actor, however, if there is a player controlled character or vehicle the parent class would be a pawn class.
Blueprints can be created from the content browser by clicking on the add new button or they can be created by placing various components within a scene, selecting said components, then converted to a blueprints class using the blueprint button in the upper middle section of the editor window.
The default scene root component is the base object which can affect its children components. This means if that root component moves, everything else moves with it. However, if you just move the child component, then the root component will stay still. You can also override the root component with another component by dragging the new root component on top of the default root component.
You can also add components or set meshes through the content browser by dragging it from the content browser to the blueprint window.
Blueprint Graphs
Construction script fire once at runtime and whenever a change is made in the editor. It can also be used for a variety of other features such as dynamically spawning in static meshes to randomly adjust how something might look. Such as a fence and the wooden planks that make up the fence, which could be spawning in different positions and rotations to create a variety of styled fences.
The event graph is the type of blueprint graph that will most likely to be used. In here we can program how the various objects work. So if we had a security camera we can use the Event Tick function (if this was Unity and C# this would be the Update function) to make the security camera move side to side, but then when the player moves within range it tracks the players movement and the security cameras light changes to red. To check if the player is within range we could use the Event Begin Overlap (if this was Unity, it would be the OnTriggerEnter function).
In the event graph we can create function graphs which can separate functionality and organise the event graph to be neater. This can be done by selecting a group of nodes, right clicking and selecting the collapse to function option. The advantage of creating a function is that it can be reused but also used and accessed within another blueprint.
You can also do the same but create macro graphs, which are similar to the functions graphs but don't require any input values. The macro graphs are more like utility functions and are accessible only in that blueprint unless a macro library is created and used. Like the function nodes, the macro nodes can be reused if needed.
You can also collapse nodes, which doesn't create a function graph or a macro graph but just helps tidy up the event graph and keeps things organise. It is important to note that these cannot be reused.
Types of blueprints
The level blueprint is used to create level specific functionality, this might be a useful for a shooting game where the player has x amount of time to escape the level before a bomb explodes as the levels before or after that one will most likely not require that functionality. The level blueprints also don't have access to the ability to add components or access the viewport like a class blueprint can.
Actor blueprints are modular blueprints that provide various functionality and can be reused throughout the level but also multiple levels. Unlike the level blueprint you can add various components to be used by the blueprint and access the viewport to move those components around if needed.
An animation blueprint is used to create the animation logic for various characters whether they are player controlled or not. A use for this is transitioning between idle, walking, jogging and running animations for the player based on the players movement speed. Unlike the actor blueprint, the animation blueprint also has another type of graph it can use. This graph is the anim graph, which can be used to set up state machines and animation transitions. The different between the anim graph and the event graph, is that the event graph controls the moment to moment variables whereas the anim graph controls the moment to moment final pose of the animation.
UMG (Unreal Motion Graphics) UI/ Widget graphs can be used to display and control a games user interface. An example of this is displaying the players health and energy bars, as well as updating them when the player takes damage or uses up stamina. It has an event graph which can provide the functionality of the UI elements. The designer tab is used to layout and add the various UI elements.
A child class inherits from a parent class, this allows us to use functionality from the parent class but also add some customisation based on the child class. An example of this would be item pickups. The parent class would have the functionality of what to do when the player hits the trigger volume but a child class would have extra functionality on top of that, so if it was a health pickup it would give the player more health where as an ammo pickup would give the player more ammo. To create a child blueprint, you go to the parent class you wish to use in the content browser and right click on it. At the top there should be an option to create a child class blueprint.
Blueprints Caveats
Blueprint Graphs
Construction script fire once at runtime and whenever a change is made in the editor. It can also be used for a variety of other features such as dynamically spawning in static meshes to randomly adjust how something might look. Such as a fence and the wooden planks that make up the fence, which could be spawning in different positions and rotations to create a variety of styled fences.
The event graph is the type of blueprint graph that will most likely to be used. In here we can program how the various objects work. So if we had a security camera we can use the Event Tick function (if this was Unity and C# this would be the Update function) to make the security camera move side to side, but then when the player moves within range it tracks the players movement and the security cameras light changes to red. To check if the player is within range we could use the Event Begin Overlap (if this was Unity, it would be the OnTriggerEnter function).
In the event graph we can create function graphs which can separate functionality and organise the event graph to be neater. This can be done by selecting a group of nodes, right clicking and selecting the collapse to function option. The advantage of creating a function is that it can be reused but also used and accessed within another blueprint.
You can also do the same but create macro graphs, which are similar to the functions graphs but don't require any input values. The macro graphs are more like utility functions and are accessible only in that blueprint unless a macro library is created and used. Like the function nodes, the macro nodes can be reused if needed.
You can also collapse nodes, which doesn't create a function graph or a macro graph but just helps tidy up the event graph and keeps things organise. It is important to note that these cannot be reused.
Types of blueprints
The level blueprint is used to create level specific functionality, this might be a useful for a shooting game where the player has x amount of time to escape the level before a bomb explodes as the levels before or after that one will most likely not require that functionality. The level blueprints also don't have access to the ability to add components or access the viewport like a class blueprint can.
Actor blueprints are modular blueprints that provide various functionality and can be reused throughout the level but also multiple levels. Unlike the level blueprint you can add various components to be used by the blueprint and access the viewport to move those components around if needed.
An animation blueprint is used to create the animation logic for various characters whether they are player controlled or not. A use for this is transitioning between idle, walking, jogging and running animations for the player based on the players movement speed. Unlike the actor blueprint, the animation blueprint also has another type of graph it can use. This graph is the anim graph, which can be used to set up state machines and animation transitions. The different between the anim graph and the event graph, is that the event graph controls the moment to moment variables whereas the anim graph controls the moment to moment final pose of the animation.
UMG (Unreal Motion Graphics) UI/ Widget graphs can be used to display and control a games user interface. An example of this is displaying the players health and energy bars, as well as updating them when the player takes damage or uses up stamina. It has an event graph which can provide the functionality of the UI elements. The designer tab is used to layout and add the various UI elements.
A child class inherits from a parent class, this allows us to use functionality from the parent class but also add some customisation based on the child class. An example of this would be item pickups. The parent class would have the functionality of what to do when the player hits the trigger volume but a child class would have extra functionality on top of that, so if it was a health pickup it would give the player more health where as an ammo pickup would give the player more ammo. To create a child blueprint, you go to the parent class you wish to use in the content browser and right click on it. At the top there should be an option to create a child class blueprint.
Blueprints Caveats
- There is a cost associated with using blueprints over native C++ code
- It is best to avoid doing complex maths or heavy operations every frame as blueprints use a virtual machine to translate the nodes into native C++ code
- Native C++ is always going to be as it doesn't require translation via virtual machine
- There is also some functionality that can only be performed using native C++ code and therefore that makes native C++ more powerful than blueprints
- Blueprints is event based and therefore require specific events to trigger the various functionality programmed in those blueprints
- Uses refererences and therefore possible to pass an invalid reference
- It is also possible to create circular dependancies, this is where two or more blueprints depend on each other and so when one blueprint is casted to another the new blueprints dependancies are loaded, which links back to the original blueprint, whose dependancies also start to load. This then kickstarts the new blueprints dependancies again and so on.
NB. You can use casting during collision checks to see if an object is a certain type, this is similar to Unity's tag system as then you can provide specific functionality if the cast succeeds or fails.
Wednesday, January 16, 2019
Sample Problem: Rotating a 2D Character
The Problem
In a top down shooter, there is a scripted in game cutscene where an explosion happens and the player must turn to face the explosion. The turn to face the explosion must be a smoothed transition and since the cutscene can be triggered in a variety of ways, the initial direction of the player can be different between playthroughs. We need to know the angle the player needs to rotate and whether the direction they need to rotate is clockwise or anticlockwise.
The Variables
The current variables we have access to is the initial direction the player is facing (Vector C) , the position of the player (P) and the position of the explosion (E).
The Solution
The first step is to calculate the direction towards the explosion from the player, which can be done by subtracting P from E. This gives us Vector N and since we don't care about the distance between the two positions, we can normalise Vector N.
Since we now have two unit vectors (Vector N and Vector C), we can calculate the angle between them by using the dot product calculation. So the angle is equal to arccos(dotProduct(Vector C, Vector N)). However, this doesn't tell us if the direction we need to rotate is clockwise or anti clockwise.
This can be done with the cross product. However, since both vectors are in 2D space, they need to be converted into 3D vectors. Once the cross product is calculated we can determine the direction of the rotation needed. If the z component of the resultant vector is positive then the rotation is anti clockwise, which means if the z component is negative then the rotation is clockwise.
In a top down shooter, there is a scripted in game cutscene where an explosion happens and the player must turn to face the explosion. The turn to face the explosion must be a smoothed transition and since the cutscene can be triggered in a variety of ways, the initial direction of the player can be different between playthroughs. We need to know the angle the player needs to rotate and whether the direction they need to rotate is clockwise or anticlockwise.
The Variables
The current variables we have access to is the initial direction the player is facing (Vector C) , the position of the player (P) and the position of the explosion (E).
The Solution
The first step is to calculate the direction towards the explosion from the player, which can be done by subtracting P from E. This gives us Vector N and since we don't care about the distance between the two positions, we can normalise Vector N.
Since we now have two unit vectors (Vector N and Vector C), we can calculate the angle between them by using the dot product calculation. So the angle is equal to arccos(dotProduct(Vector C, Vector N)). However, this doesn't tell us if the direction we need to rotate is clockwise or anti clockwise.
This can be done with the cross product. However, since both vectors are in 2D space, they need to be converted into 3D vectors. Once the cross product is calculated we can determine the direction of the rotation needed. If the z component of the resultant vector is positive then the rotation is anti clockwise, which means if the z component is negative then the rotation is clockwise.
Cross Product
Using the cross product via two vectors, a third vector is calculated. There is a single plane that contains both vectors and so the third vector is the normal to that plane or perpendicular to the plane. Since the cross product returns a normal to a plane, it only works in 3D, however, it can work with 2D vectors after they have been converted into 3D vectors by adding a zeroed z component.
The formula for the cross product is simply Vector C = Vector A * Vector B
However, this gets extended to
Vector C = (Vector A.y * Vector B.z - Vector A.z * Vector B.y, Vector A.z * Vector B.x - Vector A.x * Vector B.z, Vector A.x * Vector B.y - Vector A.y * Vector B.x)
This can be hard to remember but there is a mnemonic that reminds you of the order for the subscipts for the x component of the cross product vector, which is "xyzzy". Combine this with the base equation of c = a*b - a*b and you have the first component.
Vector C.x = Vector A.y * Vector B.z - Vector A.z * Vector B.y
You can figure out the y and z components of the cross product vector by rotating the subscripts in the following manner: x -> y -> z -> x
Vector C.y = Vector A.z * Vector B.x - Vector A.x * Vector B.z
Vector C.z = Vector A.x * Vector B.y - Vector A.y * Vector B.x
It should also be noted that the cross product is anti commutatuve, so Vector A * Vector B = -Vector B * Vector A. Another note is that if the cross product returns a vector where all three components have a value of 0 then both input vectors are collinear, which means they lie on the same line. This means a plane cannot be formed and so there is no normal for the cross product to return.
Special Usage
Since a triangle is always on a single plane, we can calculate its normal by coverting two sides of the triange to vectors, then using those vectors and the cross product we can calculate the normal. If we only care about the direction and not the magnitude of the vector then we can normalise the vector afterwards.
The formula for the cross product is simply Vector C = Vector A * Vector B
However, this gets extended to
Vector C = (Vector A.y * Vector B.z - Vector A.z * Vector B.y, Vector A.z * Vector B.x - Vector A.x * Vector B.z, Vector A.x * Vector B.y - Vector A.y * Vector B.x)
This can be hard to remember but there is a mnemonic that reminds you of the order for the subscipts for the x component of the cross product vector, which is "xyzzy". Combine this with the base equation of c = a*b - a*b and you have the first component.
Vector C.x = Vector A.y * Vector B.z - Vector A.z * Vector B.y
You can figure out the y and z components of the cross product vector by rotating the subscripts in the following manner: x -> y -> z -> x
Vector C.y = Vector A.z * Vector B.x - Vector A.x * Vector B.z
Vector C.z = Vector A.x * Vector B.y - Vector A.y * Vector B.x
It should also be noted that the cross product is anti commutatuve, so Vector A * Vector B = -Vector B * Vector A. Another note is that if the cross product returns a vector where all three components have a value of 0 then both input vectors are collinear, which means they lie on the same line. This means a plane cannot be formed and so there is no normal for the cross product to return.
Special Usage
Since a triangle is always on a single plane, we can calculate its normal by coverting two sides of the triange to vectors, then using those vectors and the cross product we can calculate the normal. If we only care about the direction and not the magnitude of the vector then we can normalise the vector afterwards.
Monday, January 14, 2019
Colour Theory
Colour can communicate emotions in a very primal way, however, due to different cultures, the symbolism of colours varies. There are constants that can be found in colour practice that help enhance the emotional messages of your designs.
A colour wheel has a primary function where the primary colours which are red, yellow and blue, sit opposite their complementary colours which are green, purple and orange. The colour wheel also has a function of displaying temperature, where it is split into half. The warm colours are between yellow to pink, whereas the cold colours are between green and purple. There are two dominate colours, the warm orange and the cold blue. Another function is a value scale, from lightest colour which is yellow to darked colour which is purple and any midrange value in between.
In theory, every colour in the spectrum can be mixed using just the three primary colours. Secondary colours are formed when mixing two primary colours together. For example, yellow and red create orange.
Complementary colours have a visual relationship where by placing each colour next to each other, the intensity of each colour increases. This allows you to attract the viewer's eye to important items, this is due the colours creating eye-catching contrast that can be likened to black against white.
Journey uses lots of warm colours that are close together on the colour wheel during the desert sections, this gives an emotional effect of harmony. However, in another part of the game, tension and unease is created by contrasting the orange and reds of the character with the complementary colours of green and blue from the envirnoment.
The colour of light must also be considered as well as the colour of objects. Light has different colour depending on the wavelength. The sun radiates warm colours such as red, orange and yellow, this makes objects seem warmer. However, if a cloud passes in from of the sun the blue light of the sky becomes a dominant light source, this causes objects to become cooler. So the dominant colour source dictates the temperature of colours within a scene. This is colour harmony, where an overall temperature unifies all the colours in an image.
Positioning colours also affects the temperatures as reds can seem cold next to blues and blues can seem warm against reds. So the combination of colours use depends on the overall light temperature in you scene.
A colour wheel has a primary function where the primary colours which are red, yellow and blue, sit opposite their complementary colours which are green, purple and orange. The colour wheel also has a function of displaying temperature, where it is split into half. The warm colours are between yellow to pink, whereas the cold colours are between green and purple. There are two dominate colours, the warm orange and the cold blue. Another function is a value scale, from lightest colour which is yellow to darked colour which is purple and any midrange value in between.
In theory, every colour in the spectrum can be mixed using just the three primary colours. Secondary colours are formed when mixing two primary colours together. For example, yellow and red create orange.
Complementary colours have a visual relationship where by placing each colour next to each other, the intensity of each colour increases. This allows you to attract the viewer's eye to important items, this is due the colours creating eye-catching contrast that can be likened to black against white.
Journey uses lots of warm colours that are close together on the colour wheel during the desert sections, this gives an emotional effect of harmony. However, in another part of the game, tension and unease is created by contrasting the orange and reds of the character with the complementary colours of green and blue from the envirnoment.
The colour of light must also be considered as well as the colour of objects. Light has different colour depending on the wavelength. The sun radiates warm colours such as red, orange and yellow, this makes objects seem warmer. However, if a cloud passes in from of the sun the blue light of the sky becomes a dominant light source, this causes objects to become cooler. So the dominant colour source dictates the temperature of colours within a scene. This is colour harmony, where an overall temperature unifies all the colours in an image.
Positioning colours also affects the temperatures as reds can seem cold next to blues and blues can seem warm against reds. So the combination of colours use depends on the overall light temperature in you scene.
Newtons Laws of Motion
Law 1
A body tends to remain at rest or continue to move in a straight line at a constant velocity unless acted upon by an external force.
Law 2
The acceleration of a body is proportional to the resultant force acting on the body, and this acceleration is in the same direction as the resultant force.
Law 3
For every force acting on a body there is an equal and opposite reacting force, where the reaction is collinear to the acting force.
A body tends to remain at rest or continue to move in a straight line at a constant velocity unless acted upon by an external force.
Law 2
The acceleration of a body is proportional to the resultant force acting on the body, and this acceleration is in the same direction as the resultant force.
Law 3
For every force acting on a body there is an equal and opposite reacting force, where the reaction is collinear to the acting force.
Sunday, January 13, 2019
Vector Math
Vector Addition
- Can be imagined as chaining vectors
- Vector C points to the position that vector A points to after being displaced by vector B
3D: Vector C = (Vector A.x + Vector B.x, Vector A.y + Vector B.y, Vector A.z + Vector B.z)
Vector Subtraction
- Can be seen as adding a negative vector
3D: Vector C = (Vector A.x - Vector B.x, Vector A.y - Vector B.y, Vector A.z - Vector B.z)
Vector Scaling
- Multiply each component of a vector by a scalar value
- Scaling only affect the length and not the direction
3D: Vector B = (Vector A.x * i, Vector A.y * i, Vector A.z * i)
Vector Division
- Same as vector scaling but you divide each component by a divisor
Vector Length
- Calculates the magnitude of the vector
2D: Length = square root(Vector A.x^2 + Vector A.y^2)
3D: Length = square root (Vector A.x^2 + Vector A.y^2 + Vector A.z^2)
Unit Vector
- Is any vector with a length of 1
- Also known as normalisation
- Can be calculated by dividing each component by the vectors length
2D: Unit Vector = (Vector A.x / ||Vector A||, Vector A.y / ||Vector A||)
3D: Unit Vector = (Vector A.x / ||Vector A||, Vector A.y / ||Vector A||, Vector A.z / ||Vector A||)
Friday, January 11, 2019
Sample Problem: Vector Reflection
Say we have a scenario, where a ball is moving towards a wall, if the ball hits a wall then it will reflect off of it. If the wall is parallel to an axis then this is fairly simple, we just invert the velocity of the ball in the axis perpendicular to the walls axis. For example, if the wall is on the x axis we would invert the velocity of the ball in the y axis. However, its not that simple should the wall not be parallel.
The formula to solve this is: Vi = Vo - 2n(Vo * n)
where Vi is the final velocity after the ball has been reflected, Vo is the initial velocity before the ball has been reflected and n is the normal of the wall that the ball will reflect on. All of which are vectors.
How this formula is derived
At the start of the problem we know two variables, the inital velocity and the normal of the wall, which is a unit vector. If we had a vector that started at the tail of the inital velocity and ended at the head of the final velocity, then we could use vector subtraction to figure out what the final velocity is. The parallel vector that shows the distance the ball will travel from its inital point to the wall is s and we can presume that it will travel the same after the bounce therefore our vector we need for the subtraction is 2s. This means we need to find s.
So currently Vi = 2s - Vo
To find s we would need to extend n so that it would reach the head of the s vector. This can be achieved by using vector projection and inverting the inital velocity to find the distance. Doing so gives us -Vo * n. As n is a unit vector we can multiply n by the scalar distance we just found. This gives us extended n = n * (-Vo * n). Then by using vector addition we can find s, so s = Vo + n * (-Vo * n).
Now that we have the value of s we can subsitute that value into our original equation and then simplfy.
Vi = 2(Vo + n * (-Vo * n)) - Vo
Vi = 2Vo + 2n * (-Vo * n) - V0
Vi = Vo + 2n * (-Vo * n)
Vi = Vo - 2n * (Vo * n)
Below are diagrams to help explain the process.
The formula to solve this is: Vi = Vo - 2n(Vo * n)
where Vi is the final velocity after the ball has been reflected, Vo is the initial velocity before the ball has been reflected and n is the normal of the wall that the ball will reflect on. All of which are vectors.
How this formula is derived
At the start of the problem we know two variables, the inital velocity and the normal of the wall, which is a unit vector. If we had a vector that started at the tail of the inital velocity and ended at the head of the final velocity, then we could use vector subtraction to figure out what the final velocity is. The parallel vector that shows the distance the ball will travel from its inital point to the wall is s and we can presume that it will travel the same after the bounce therefore our vector we need for the subtraction is 2s. This means we need to find s.
So currently Vi = 2s - Vo
To find s we would need to extend n so that it would reach the head of the s vector. This can be achieved by using vector projection and inverting the inital velocity to find the distance. Doing so gives us -Vo * n. As n is a unit vector we can multiply n by the scalar distance we just found. This gives us extended n = n * (-Vo * n). Then by using vector addition we can find s, so s = Vo + n * (-Vo * n).
Now that we have the value of s we can subsitute that value into our original equation and then simplfy.
Vi = 2(Vo + n * (-Vo * n)) - Vo
Vi = 2Vo + 2n * (-Vo * n) - V0
Vi = Vo + 2n * (-Vo * n)
Vi = Vo - 2n * (Vo * n)
Below are diagrams to help explain the process.
Thursday, January 10, 2019
2D Collision Detection Pt 1
Rectangle - Rectangle Collision Detection
For rectangle - rectangle collisions, a collision can be determined if there is an overlap in both dimensions.
Pseudo Code
bool Overlap(float _minA, float _maxA, float _minB, float _maxB)
{
return _minB <= _maxA && _minA <= _maxB;
}
bool RectsCollide(Rect _rectA, Rect _rectB)
{
float aLeft = _rectA.origin.x;
float aRight = aLeft + _rectA.size.x;
float bLeft = _rectB.origin.x;
float bRight = bLeft + _rectB.size.x;
float aBottom = _rectA.origin.y;
float aTop = aBottom + _rectA.size.y;
float bBottom = _rectB.origin.y;
float bTop = bBottom + _rectB.size.y;
return Overlap(aLeft, aRight, bLeft, bRight) && Overlap(aBottom, aTop, bBottom, bTop);
}
Circle - Circle Collision Detection
To detect circle-circle collisions, we must compare the distance between their centers and the sum of both circles radii. If the distance is less than the sum then there is a collision.
Pseudo Code
bool CircleCollide(Circle _circA, Circle _circB)
{
float radiusSum = _circA.radius + _circB.radius;
Vector2 distance = SubtractVectors(_circA.center, _circB.center);
return VectorLength(distance) <= radiusSum;
}
Line - Line Collision Detection
Lines are endless and therefore there are only two scenarios where lines don't collide: when they are parallel and not equivalent.
Pseudo Code
Vector2 RotateVector90(Vector2 _vector)
{
return new Vector2(-_vector.y, _vector.x);
}
bool ParallelVectors(Vector2 _vectorA, Vector2 _vectorB)
{
Vector2 rotatedA = RotateVector90(_vectorA);
return DotProduct(rotatedA, _vectorB) == 0;
}
bool EqualVectors(Vector2 _vectorA, Vector2 _vectorB)
{
return (_vectorA.x - _vectorB.x == 0) && (_vectorA.y - _vectorB.y == 0)
}
bool EquivalentLines(Line _lineA, Line _lineB)
{
if (!ParallelVectors(_lineA.direction, _lineB.direction))
{
return false;
}
Vector2 distance = SubtractVectors(_lineA.base, _lineB.base)
return ParallelVectors(distance, a.direction);
}
bool LinesCollide(Line _lineA, Line _lineB)
{
if (ParallelVectors(_lineA.direction, _lineB.direction))
{
return EquivalentLines(_lineA, _lineB);
}
else
{
return true;
}
}
The function ParallelVectors() allows us to check for parallelism between two vectors and we know that if the returned value from a calculated dot product is 0 then the two vectors have a 90° angle between themselves. So we rotate one of the vectors by 90° and check their dot product to find out if they are parallel.
Equivalence in this scenario doesn't mean that the two lines are equal, in fact it means that the two lines have a parallel direction to each other and one of the base points is somewhere on the other line.
The function EquivalentLines() checks for parallelism, so if the lines are parallel we need to find out if the a base point of one line lies on the other line. This means that the distance vector is parallel to either lines.
The function LineCollide() checks if they are parallel and if that is the case then returns their equivalence. If they are not parallel then they do collide.
For rectangle - rectangle collisions, a collision can be determined if there is an overlap in both dimensions.
Pseudo Code
bool Overlap(float _minA, float _maxA, float _minB, float _maxB)
{
return _minB <= _maxA && _minA <= _maxB;
}
bool RectsCollide(Rect _rectA, Rect _rectB)
{
float aLeft = _rectA.origin.x;
float aRight = aLeft + _rectA.size.x;
float bLeft = _rectB.origin.x;
float bRight = bLeft + _rectB.size.x;
float aBottom = _rectA.origin.y;
float aTop = aBottom + _rectA.size.y;
float bBottom = _rectB.origin.y;
float bTop = bBottom + _rectB.size.y;
return Overlap(aLeft, aRight, bLeft, bRight) && Overlap(aBottom, aTop, bBottom, bTop);
}
Circle - Circle Collision Detection
To detect circle-circle collisions, we must compare the distance between their centers and the sum of both circles radii. If the distance is less than the sum then there is a collision.
Pseudo Code
bool CircleCollide(Circle _circA, Circle _circB)
{
float radiusSum = _circA.radius + _circB.radius;
Vector2 distance = SubtractVectors(_circA.center, _circB.center);
return VectorLength(distance) <= radiusSum;
}
Line - Line Collision Detection
Lines are endless and therefore there are only two scenarios where lines don't collide: when they are parallel and not equivalent.
Pseudo Code
Vector2 RotateVector90(Vector2 _vector)
{
return new Vector2(-_vector.y, _vector.x);
}
bool ParallelVectors(Vector2 _vectorA, Vector2 _vectorB)
{
Vector2 rotatedA = RotateVector90(_vectorA);
return DotProduct(rotatedA, _vectorB) == 0;
}
bool EqualVectors(Vector2 _vectorA, Vector2 _vectorB)
{
return (_vectorA.x - _vectorB.x == 0) && (_vectorA.y - _vectorB.y == 0)
}
bool EquivalentLines(Line _lineA, Line _lineB)
{
if (!ParallelVectors(_lineA.direction, _lineB.direction))
{
return false;
}
Vector2 distance = SubtractVectors(_lineA.base, _lineB.base)
return ParallelVectors(distance, a.direction);
}
bool LinesCollide(Line _lineA, Line _lineB)
{
if (ParallelVectors(_lineA.direction, _lineB.direction))
{
return EquivalentLines(_lineA, _lineB);
}
else
{
return true;
}
}
The function ParallelVectors() allows us to check for parallelism between two vectors and we know that if the returned value from a calculated dot product is 0 then the two vectors have a 90° angle between themselves. So we rotate one of the vectors by 90° and check their dot product to find out if they are parallel.
Equivalence in this scenario doesn't mean that the two lines are equal, in fact it means that the two lines have a parallel direction to each other and one of the base points is somewhere on the other line.
The function EquivalentLines() checks for parallelism, so if the lines are parallel we need to find out if the a base point of one line lies on the other line. This means that the distance vector is parallel to either lines.
The function LineCollide() checks if they are parallel and if that is the case then returns their equivalence. If they are not parallel then they do collide.
Dot Product
The dot product describes the relation between two vectors with a single number. The single number can mean three possible things about the angle between the two vectors.
- If the value was 0 then the angle between the two vectors is 90°
- If the value is positive so greater than 0, then the angle between the two vectors is less than 90°
- If the value is negative so less than 0, then the angle between the two vectors is greater than 90°
Pseudo code
float DotProduct(Vector2 _vectorA, Vector2 _vectorB)
{
return _vectorA.x * _vectorB.x + _vectorA.y * _vectorB.y;
}
The dot product can be used for more though as using the dot product with the unit vectors of any two given vectors can result in the actual angle between the two vectors.
- First find the unit vector of both given vectors
- Calculate the dot product of the two unit vectors
- Use arc cosine (dot product) to calculate the angle in radians
- Convert angle to degrees if needed
Also the dot product of two vectors that are the same equals the length of the vector squared. This can be used for geometric calculations. Another side note if you have a unit vector and a non unit vector you can perform scalar projects, which returns the length of the extended unit vector (Unit Vector * Non Unit Vector).
Subscribe to:
Posts (Atom)