Unreal Engine 5 Gameplay Framework
Basics of Computer Hardware and Software
How Memory Works
- RAM (Random Access Memory):
- Definition: Volatile memory used for temporary storage while the computer is running.
- Purpose: Stores data that is actively being used or processed by the CPU.
- Characteristics: Fast read and write speeds, but data is lost when the power is turned off.
- Example Use: Running applications, open files, and active processes.
- ROM (Read-Only Memory):
- Definition: Non-volatile memory used for permanent storage.
- Purpose: Stores firmware and essential system programs that do not change frequently.
- Characteristics: Data is retained even when the power is turned off, typically not writable or only writable under specific conditions.
- Example Use: BIOS or UEFI firmware in computers, embedded systems.
Heap vs. Stack
- Stack:
- Definition: Memory allocated for automatic variables and function call management.
- Structure: Operates in a last-in, first-out (LIFO) manner.
- Purpose: Used for static memory allocation, such as local variables and function call management.
- Characteristics: Fast access, but limited in size and scope.
- Example Use: Local variables in functions, function call stack.
- Heap:
- Definition: Memory allocated dynamically during runtime.
- Structure: Managed by the programmer or garbage collector, not structured like the stack.
- Purpose: Used for dynamic memory allocation, such as objects and data that need to persist beyond a single function call.
- Characteristics: Larger size compared to stack, but slower access due to more complex management.
- Example Use: Objects created with
new
ormalloc
, dynamic data structures like linked lists.
How CPU Works
- Fetch-Decode-Execute Cycle:
- Fetch: The CPU retrieves an instruction from the program memory.
- Decode: The instruction is translated into signals that control other parts of the CPU.
- Execute: The CPU performs the operation defined by the instruction.
- Registers:
- Definition: Small, fast storage locations within the CPU.
- Purpose: Temporarily hold data and instructions that are being used or processed.
- Types: General-purpose registers (for arithmetic and data storage) and special-purpose registers (for specific tasks like instruction pointers).
- Cache:
- Definition: A small, high-speed memory located inside or very close to the CPU.
- Purpose: Stores frequently accessed data and instructions to speed up processing.
- Levels: Typically organized in levels (L1, L2, L3) with L1 being the fastest and smallest.
How GPU Works
- Parallel Processing:
- Definition: The ability to perform many operations simultaneously.
- Purpose: Handles tasks that can be divided into smaller, parallel tasks, such as rendering graphics and performing complex mathematical calculations.
- Differences between CPU and GPU:
- CPU: Optimized for single-threaded performance and general-purpose tasks.
- GPU: Optimized for multi-threaded performance and tasks that require parallel processing.
Communication Between CPU, GPU, and Memory
- Bus Architecture:
- Definition: A communication system that transfers data between components inside a computer.
- Types: Address bus (carries memory addresses), data bus (carries data), and control bus (carries control signals).
- Data Transfer Methods:
- Direct Memory Access (DMA): Allows peripherals to directly read/write memory without CPU intervention, speeding up data transfer.
- Memory-Mapped I/O: Uses the same address space for memory and I/O devices, allowing the CPU to interact with hardware using standard read/write operations.
How Compilers Work
- Source Code to Machine Code:
- Definition: The process of translating high-level programming code into machine code that the CPU can execute.
- Lexical Analysis:
- Definition: The first phase of compilation where the source code is converted into tokens.
- Purpose: Simplifies the syntax analysis by breaking down the source code into manageable pieces.
- Example: Keywords, operators, identifiers.
- Syntax Analysis:
- Definition: The second phase where the tokens are arranged into a tree structure called a syntax tree.
- Purpose: Ensures that the tokens form valid statements according to the language’s grammar.
- Semantic Analysis:
- Definition: The phase where the compiler adds semantic meaning to the syntax tree.
- Purpose: Checks for semantic errors such as type mismatches and undefined variables.
- Code Optimization:
- Definition: The process of improving the intermediate code to make it run more efficiently.
- Purpose: Enhances the performance and reduces the resource consumption of the compiled code.
- Code Generation:
- Definition: The final phase where the optimized intermediate code is translated into machine code.
- Purpose: Produces the final executable program that can run on the target machine.
How Linking Works
- Static Linking:
- Definition: The process of combining all the program’s object files into a single executable file during compilation.
- Purpose: Produces an executable that includes all necessary code and libraries.
- Characteristics: Larger executable size, no dependencies on external libraries at runtime.
- Dynamic Linking:
- Definition: The process of linking the program to external libraries at runtime rather than during compilation.
- Purpose: Reduces the executable size and allows multiple programs to share the same library code.
- Characteristics: Smaller executable size, dependencies on external libraries at runtime.
- Linker Responsibilities:
- Symbol Resolution: Matches function calls with the correct function definitions.
- Relocation: Adjusts the addresses of symbols in the code to their final locations in memory.
Programming Paradigms
Procedural Programming
- Definition: A paradigm based on the concept of procedure calls, where statements are structured into procedures (also known as routines, subroutines, or functions).
- Key Features: Sequential execution, use of loops and conditionals, modularity.
- Examples: C, Pascal.
Object-Oriented Programming (OOP)
- Classes:
- Definition: Blueprints for creating objects, encapsulating data and methods.
- Objects:
- Definition: Instances of classes that interact with one another.
- Inheritance:
- Definition: A mechanism where one class can inherit traits (properties and methods) from another class.
- Polymorphism:
- Definition: The ability of different classes to be treated as instances of the same class through inheritance.
- Types: Compile-time (method overloading) and runtime (method overriding).
- Encapsulation:
- Definition: The bundling of data with methods that operate on the data, restricting direct access to some of the object’s components.
- Abstraction:
- Definition: The concept of hiding complex implementation details and showing only the necessary features of an object.
Functional Programming
- Pure Functions:
- Definition: Functions that have no side effects and return the same result given the same input.
- Immutability:
- Definition: Data cannot be modified after it is created.
- Higher-Order Functions:
- Definition: Functions that take other functions as arguments or return them as results.
Event-Driven Programming
- Definition: A paradigm where the flow of the program is determined by events such as user actions (mouse clicks, key presses), sensor outputs, or message passing.
- Key Features: Event handlers, event loops, callbacks.
- Examples: JavaScript (with DOM events), Node.js, GUI applications.
Basic Programming Concepts
Variables and Data Types
- Variables:
- Definition: Named storage locations in memory for holding data.
- Example:
int age = 25;
- Data Types:
- Primitive Types: Integer, float, double, char, boolean.
- Composite Types: Arrays, structs, objects.
- Example:
int, float, char, boolean
.
Operators
- Arithmetic Operators:
- Definition: Perform mathematical operations.
- Example:
+ (addition), - (subtraction), * (multiplication), / (division), % (modulus)
- Comparison Operators:
- Definition: Compare two values.
- Example:
== (equal to), != (not equal to), > (greater than), < (less than), >= (greater than or equal to), <= (less than or equal to)
- Logical Operators:
- Definition: Perform logical operations.
- Example:
&& (logical AND), || (logical OR), ! (logical NOT)
- Assignment Operators:
- Definition: Assign values to variables.
- Example:
= (assignment), += (addition assignment), -= (subtraction assignment), *= (multiplication assignment), /= (division assignment)
Control Structures
If Statements:
- Definition: Execute a block of code if a specified condition is true.
C++:
if (condition) {
// code to be executed if condition is true
}
Python:
if condition:
# code to be executed if condition is true
Loops:
- For Loop:
- Definition: Repeats a block of code a specified number of times.
C++:
for (int i = 0; i < 10; i++) {
// code to be executed
}
Python:
for i in range(10):
# code to be executed
- While Loop:
- Definition: Repeats a block of code as long as a specified condition is true.
C++:
while (condition) {
// code to be executed
}
Python:
while condition:
# code to be executed
- Do-While Loop:
- Definition: Similar to the while loop, but checks the condition after executing the block of code.
C++:
do {
// code to be executed
} while (condition);
Python: Python does not have a built-in do-while loop. You can simulate it using a while loop with a break condition:
while True:
# code to be executed
if not condition:
break
- Switch-Case:
- Definition: Selects one of many code blocks to be executed.
C++:
switch (expression) {
case value1:
// code to be executed if expression == value1
break;
case value2:
// code to be executed if expression == value2
break;
default:
// code to be executed if expression doesn't match any case
}
Python: Python does not have a built-in switch-case statement. You can use if-elif-else statements to achieve similar functionality:
if expression == value1:
# code to be executed if expression == value1
elif expression == value2:
# code to be executed if expression == value2
else:
# code to be executed if expression doesn't match any case
Functions/Methods
- Definition: Blocks of code that perform a specific task, can be called upon to execute when needed.
- Purpose: Promote code reusability and modularity.
C++:
int add(int a, int b) {
return a + b;
}
Python:
def add(a, b):
return a + b
Data Structures
Arrays and Lists
Arrays:
- Definition: A collection of elements, all of the same type, stored in contiguous memory locations.
- Characteristics: Fixed size, direct access by index.
C++:
int numbers[5] = {1, 2, 3, 4, 5};
Python:
numbers = [1, 2, 3, 4, 5]
Lists:
- Definition: A collection of elements that can be of any type and are dynamically sized.
- Characteristics: Flexible size, often implemented using linked structures.
Stacks and Queues
Stacks:
- Definition: A collection of elements that follows the Last In, First Out (LIFO) principle.
- Operations: Push (add an element), Pop (remove the top element), Peek (view the top element).
C++:
#include
std::stack stack;
stack.push(1); // Push
stack.pop(); // Pop
Python:
stack = []
stack.append(1) # Push
stack.pop() # Pop
Queues:
- Definition: A collection of elements that follows the First In, First Out (FIFO) principle.
- Operations: Enqueue (add an element), Dequeue (remove the front element), Peek (view the front element).
C++:
#include
std::queue queue;
queue.push(1); // Enqueue
queue.pop(); // Dequeue
Python:
from collections import deque
queue = deque()
queue.append(1) # Enqueue
queue.popleft() # Dequeue
Linked Lists
Singly Linked Lists:
- Definition: A collection of nodes where each node contains data and a reference to the next node in the sequence.
- Characteristics: Allows dynamic memory allocation, efficient insertion, and deletion.
C++:
struct Node {
int data;
Node* next;
Node(int val) : data(val), next(nullptr) {}
};
class LinkedList {
public:
Node* head;
LinkedList() : head(nullptr) {}
};
Python:
class Node:
def __init__(self, data):
self.data = data
self.next = None
class LinkedList:
def __init__(self):
self.head = None
Doubly Linked Lists:
- Definition: Similar to singly linked lists, but each node also contains a reference to the previous node.
- Characteristics: Allows traversal in both directions.
C++:
struct Node {
int data;
Node* next;
Node* prev;
Node(int val) : data(val), next(nullptr), prev(nullptr) {}
};
class DoublyLinkedList {
public:
Node* head;
DoublyLinkedList() : head(nullptr) {}
};
Python:
class Node:
def __init__(self, data):
self.data = data
self.next = None
self.prev = None
class DoublyLinkedList:
def __init__(self):
self.head = None
Trees and Graphs
Binary Tree:
- Definition: A tree data structure where each node has at most two children (left and right).
- Characteristics: Hierarchical structure, efficient searching and sorting.
C++:
struct TreeNode {
int data;
TreeNode* left;
TreeNode* right;
TreeNode(int val) : data(val), left(nullptr), right(nullptr) {}
};
Python:
class TreeNode:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
Binary Search Tree:
- Definition: A binary tree where the left child of a node contains only nodes with values less than the parent node, and the right child contains only nodes with values greater than the parent node.
- Characteristics: Efficient searching, insertion, and deletion.
AVL Tree:
- Definition: A self-balancing binary search tree where the difference in heights between the left and right subtrees cannot be more than one for all nodes.
- Characteristics: Ensures O(log n) time complexity for search, insertion, and deletion.
Graphs:
- Definition: A collection of nodes (vertices) and edges connecting pairs of nodes.
- Types: Directed, undirected, weighted, unweighted.
Hash Tables
- Definition: A data structure that maps keys to values for highly efficient lookup.
- Characteristics: Utilizes a hash function to compute an index into an array of buckets or slots, from which the desired value can be found.
C++:
#include
std::unordered_map hash_table;
hash_table["key"] = "value";
Python:
hash_table = {}
hash_table['key'] = 'value'
C++:
Algorithms
Searching Algorithms
Linear Search:
- Definition: Iterates through each element in a list until the target element is found or the list ends.
- Characteristics: Simple but inefficient for large datasets (O(n) time complexity).
C++:
int linear_search(int arr[], int size, int target) {
for (int i = 0; i < size; i++) {
if (arr[i] == target) {
return i;
}
}
return -1;
}
Python:
def linear_search(arr, target):
for index, value in enumerate(arr):
if value == target:
return index
return -1
Binary Search:
- Definition: Efficiently searches a sorted list by repeatedly dividing the search interval in half.
- Characteristics: Requires sorted input, O(log n) time complexity.
C++:
int binary_search(int arr[], int size, int target) {
int left = 0, right = size - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (arr[mid] == target) {
return mid;
} else if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
Python:
def binary_search(arr, target):
left, right = 0, len(arr) - 1
while left <= right:
mid = (left + right) // 2
if arr[mid] == target:
return mid
elif arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return -1
Sorting Algorithms
Bubble Sort:
- Definition: Repeatedly steps through the list, compares adjacent elements, and swaps them if they are in the wrong order.
- Characteristics: Simple but inefficient for large datasets (O(n^2) time complexity).
C++:
void bubble_sort(int arr[], int size) {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
std::swap(arr[j], arr[j + 1]);
}
}
}
}
Python:
def bubble_sort(arr):
n = len(arr)
for i in range(n):
for j in range(0, n-i-1):
if arr[j] > arr[j+1]:
arr[j], arr[j+1] = arr[j+1], arr[j]
Selection Sort:
- Definition: Divides the list into a sorted and an unsorted region, repeatedly selects the smallest element from the unsorted region, and moves it to the end of the sorted region.
- Characteristics: Inefficient for large datasets (O(n^2) time complexity).
C++:
void selection_sort(int arr[], int size) {
for (int i = 0; i < size; i++) {
int min_index = i;
for (int j = i + 1; j < size; j++) {
if (arr[j] < arr[min_index]) {
min_index = j;
}
}
std::swap(arr[i], arr[min_index]);
}
}
Python:
def selection_sort(arr):
n = len(arr)
for i in range(n):
min_index = i
for j in range(i+1, n):
if arr[j] < arr[min_index]:
min_index = j
arr[i], arr[min_index] = arr[min_index], arr[i]
Insertion Sort:
- Definition: Builds the final sorted list one item at a time by repeatedly inserting the next item into the correct position.
- Characteristics: Efficient for small datasets or nearly sorted lists (O(n^2) time complexity in the worst case, O(n) in the best case).
C++:
void insertion_sort(int arr[], int size) {
for (int i = 1; i < size; i++) {
int key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
Python:
def insertion_sort(arr):
for i in range(1, len(arr)):
key = arr[i]
j = i - 1
while j >= 0 and key < arr[j]:
arr[j + 1] = arr[j]
j -= 1
arr[j + 1] = key
Quicksort:
- Definition: Divides the list into two smaller sub-lists, the low elements and the high elements, and then recursively sorts the sub-lists.
- Characteristics: Efficient for large datasets (average O(n log n) time complexity).
C++:
#include
std::vector quicksort(std::vector& arr) {
if (arr.size() <= 1) {
return arr;
}
int pivot = arr[arr.size() / 2];
std::vector left, middle, right;
for (int x : arr) {
if (x < pivot) {
left.push_back(x);
} else if (x == pivot) {
middle.push_back(x);
} else {
right.push_back(x);
}
}
std::vector sorted_left = quicksort(left);
std::vector sorted_right = quicksort(right);
sorted_left.insert(sorted_left.end(), middle.begin(), middle.end());
sorted_left.insert(sorted_left.end(), sorted_right.begin(), sorted_right.end());
return sorted_left;
}
Python:
def quicksort(arr):
if len(arr) <= 1:
return arr
pivot = arr[len(arr) // 2]
left = [x for x in arr if x < pivot]
middle = [x for x in arr if x == pivot]
right = [x for x in arr if x > pivot]
return quicksort(left) + middle + quicksort(right)
Mergesort:
- Definition: Divides the list into two halves, recursively sorts each half, and then merges the two sorted halves.
- Characteristics: Stable sort with O(n log n) time complexity, uses additional memory for merging.
C++:
#include
std::vector merge(const std::vector& left, const std::vector& right) {
std::vector result;
size_t i = 0, j = 0;
while (i < left.size() && j < right.size()) {
if (left[i] < right[j]) {
result.push_back(left[i]);
i++;
} else {
result.push_back(right[j]);
j++;
}
}
while (i < left.size()) {
result.push_back(left[i]);
i++;
}
while (j < right.size()) {
result.push_back(right[j]);
j++;
}
return result;
}
std::vector merge_sort(const std::vector& arr) {
if (arr.size() <= 1) {
return arr;
}
size_t mid = arr.size() / 2;
std::vector left(arr.begin(), arr.begin() + mid);
std::vector right(arr.begin() + mid, arr.end());
return merge(merge_sort(left), merge_sort(right));
}
Python:
def merge_sort(arr):
if len(arr) <= 1:
return arr
mid = len(arr) // 2
left = merge_sort(arr[:mid])
right = merge_sort(arr[mid:])
return merge(left, right)
def merge(left, right):
result = []
i = j = 0
while i < len(left) and j < len(right):
if left[i] < right[j]:
result.append(left[i])
i += 1
else:
result.append(right[j])
j += 1
result.extend(left[i:])
result.extend(right[j:])
return result
Recursion
- Definition: A process where a function calls itself as a subroutine.
- Purpose: Useful for tasks that can be divided into similar subtasks.
- Characteristics: Must have a base case to avoid infinite recursion.
C++:
int factorial(int n) {
if (n == 0) {
return 1;
} else {
return n * factorial(n - 1);
}
}
Python:
def factorial(n):
if n == 0:
return 1
else:
return n * factorial(n-1)
Dynamic Programming
- Definition: A method for solving complex problems by breaking them down into simpler subproblems and storing the results of subproblems to avoid redundant computations.
- Purpose: Optimizes recursive algorithms by storing intermediate results.
- Characteristics: Uses a table to store results of subproblems.
C++:
#include
int fibonacci(int n, std::unordered_map& memo) {
if (memo.find(n) != memo.end()) {
return memo[n];
}
if (n <= 1) {
return n;
}
memo[n] = fibonacci(n - 1, memo) + fibonacci(n - 2, memo);
return memo[n];
}
int fibonacci(int n) {
std::unordered_map memo;
return fibonacci(n, memo);
}
Python:
def fibonacci(n, memo={}):
if n in memo:
return memo[n]
if n <= 1:
return n
memo[n] = fibonacci(n-1, memo) + fibonacci(n-2, memo)
return memo[n]
Error Handling
Exceptions
- Definition: Events that occur during the execution of a program that disrupt the normal flow of instructions.
- Types of Exceptions:
- Syntax Errors: Errors in the structure of the code, detected at compile time.
- Runtime Errors: Errors that occur during program execution, such as division by zero or file not found.
- Logical Errors: Errors in the logic of the program that produce incorrect results but do not stop the program from running.
- Handling Exceptions:
- Try-Except Block: Code that might raise an exception is placed in a
try
block, and code to handle the exception is placed in anexcept
block.
- Try-Except Block: Code that might raise an exception is placed in a
C++:
Note: C++ does not throw an exception for division by zero in integers; it causes undefined behavior. you might need to explicitly check for zero and throw an exception manually.
#include
#include
int main() {
try {
int denominator = 0;
if (denominator == 0) {
throw std::runtime_error("Cannot divide by zero!");
}
int result = 10 / denominator;
} catch (const std::runtime_error& e) {
std::cout << e.what() << std::endl;
}
return 0;
}
Python:
try:
result = 10 / 0
except ZeroDivisionError:
print("Cannot divide by zero!")
Try-Except-Else Block: The else
block executes if no exceptions are raised.
C++:
#include
#include
int main() {
try {
int denominator = 2;
if (denominator == 0) {
throw std::runtime_error("Cannot divide by zero!");
}
int result = 10 / denominator;
std::cout << "Division successful: " << result << std::endl;
} catch (const std::runtime_error& e) {
std::cout << e.what() << std::endl;
}
return 0;
}
Python:
try:
result = 10 / 2
except ZeroDivisionError:
print("Cannot divide by zero!")
else:
print("Division successful:", result)
Try-Except-Finally Block: The finally
block executes regardless of whether an exception was raised or not.
C++ does not have a finally
block.
Python:
try:
file = open("test.txt", "r")
content = file.read()
except FileNotFoundError:
print("File not found!")
finally:
file.close()
Debugging
- Definition: The process of identifying, analyzing, and removing errors in a program.
- Common Debugging Techniques:
- Print Statements: Inserting print statements to output variable values and program flow to the console.
C++:
#include
int add(int a, int b) {
std::cout << "a: " << a << ", b: " << b << std::endl;
return a + b;
}
Python:
def add(a, b):
print(f"a: {a}, b: {b}")
return a + b
- Using a Debugger: A tool that allows step-by-step execution of a program, inspection of variable values, and setting breakpoints.
- Example: Debuggers in IDEs like Visual Studio, PyCharm, or Eclipse.
- Code Reviews: Having peers review your code to catch errors and improve code quality.
- Unit Testing: Writing tests for individual units of code to ensure they work as expected.
C++:
#include
#include
int add(int a, int b) {
std::cout << "a: " << a << ", b: " << b << std::endl;
return a + b;
}
void test_add() {
assert(add(2, 3) == 5);
assert(add(-1, 1) == 0);
std::cout << "All tests passed!" << std::endl;
}
int main() {
test_add();
return 0;
}
Python:
def test_add():
assert add(2, 3) == 5
assert add(-1, 1) == 0