0%

关于Unity的笔记 - 续

因为上一篇太乱了没看懂于是新开一篇(靠).




Temp

1
[RequireComponent(typeof(CharacterController))]

Get Instance.

1
2
3
4
5
6
7
8
9
10
private PlayerInput _input;
private CharacterController _characterController;

void Start()
{
// dynamicly finding it -> because it is singleton?
_input = PlayerInput.GetInstance();
// this is finding the component attached to the same game object
_characterController = GetComponent<CharacterController>();
}
1
2
3
4
void GroundCheck()
{
_isGrounded = Physics.CheckSphere(_groundCheck.position, _groundCheckDistance, _groundMask);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public abstract class Interactor : MonoBehaviour
{
protected PlayerInput _input;

private void Start()
{
_input = PlayerInput.GetInstance();
}

private void Update()
{
Interact();
}

public abstract void Interact();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class SimpleInteractor : Interactor
{
[Header("Interact")]
[SerializeField] private Camera _cam;
[SerializeField] private LayerMask _interactionLayer;
[SerializeField] private float _interactionDistance;

private RaycastHit _raycastHit;
protected ISelectable _selectable;

public override void Interact() {
Ray ray = _cam.ScreenPointToRay(new Vector3(Screen.width / 2, Screen.height / 2, 0)); // **Physics.Raycast**
if (Physics.Raycast(ray, out _raycastHit, _interactionDistance, _interactionLayer))
{
_selectable = _raycastHit.transform.GetComponent<ISelectable>();
if (_selectable != null) {
_selectable.OnHoverEnter();

if (_input._interactPressed) {
_selectable.OnSelect();
}
}
}

if (_raycastHit.transform == null && _selectable != null)
{
_selectable.OnHoverExit();
_selectable = null;
}


}

}
1
2
3
4
5
public enum Input
{
Primary,
Secondary
}

TODO -> search it up

IPointerEnterHandler
IPointerExitHandler
IPointerDownHandler
IPointerUpHandler
IPointerClickHandler

ScriptableObejct



Code

Action

1
2
3
4
5
6
7
8
9
10
11
12
13
using UnityEngine.Events;

public Action<float> OnHealthUpdated;
// this means onHealthUpdated func will take float as parameter
public Action OnDeath;
// this does not take parameter

OnHealthUpdated += AnotherFunc_OnHealthUpdate;

void AnotherFunc_OnHealthUpdate(float health){
// actions
// can be different script
}


Unity Events

1



Generics

可以用来代表不定的parameter.

1
2
3
4
5
6
7
8
9
10
11
12
13
using UnityEngine;
using System.Collections;

public class SomeClass
{
//Here is a generic method. Notice the generic
//type 'T'. This 'T' will be replaced at runtime
//with an actual type.
public T GenericMethod<T>(T param)
{
return param;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
using UnityEngine;
using System.Collections;

public class SomeOtherClass : MonoBehaviour
{
void Start ()
{
SomeClass myClass = new SomeClass();

//In order to use this method you must
//tell the method what type to replace
//'T' with.
myClass.GenericMethod<int>(5);
}
}



Collision / Trigger

OnCollisionEnter OnCollisionEnter is called when this collider/rigidbody has begun touching another rigidbody/collider.
OnCollisionExit OnCollisionExit is called when this collider/rigidbody has stopped touching another rigidbody/collider.
OnCollisionStay OnCollisionStay is called once per frame for every Collider or Rigidbody that touches another Collider or Rigidbody.
OnTriggerEnter When a GameObject collides with another GameObject, Unity calls OnTriggerEnter.
OnTriggerExit OnTriggerExit is called when the Collider other has stopped touching the trigger.
OnTriggerStay OnTriggerStay is called almost all the frames for every Collider other that is touching the trigger. The function is on the physics timer so it won’t necessarily run every frame.


Joints

也有2Djoints.



Observer Design Pattern

UnityEvents and Actions are Unity-specific implementations of the Observer pattern.
● AddListener and RemoveListener are the subscription functions.
● Invoking the Action/UnityEvent is the notify behavior.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
using System;
using System.Collections.Generic;

// Observer interface
public interface IObserver
{
void Update(double stockPrice);
}

// Concrete Observer class
public class Investor : IObserver
{
private string name;

public Investor(string name)
{
this.name = name;
}

public void Update(double stockPrice)
{
Console.WriteLine($"{name} received an update:
Stock price is now {stockPrice:C}");
}
}

// Subject (Observable) class
public class Stock
{
private double price;
private List<IObserver> investors = new List<IObserver>();

public void Attach(IObserver observer)
{
investors.Add(observer);
}

public void Detach(IObserver observer)
{
investors.Remove(observer);
}

public void SetPrice(double newPrice)
{
price = newPrice;
Notify();
}

private void Notify()
{
foreach (var investor in investors)
{
investor.Update(price);
}
}
}

// Usage
class Program
{
static void Main()
{
Stock techStock = new Stock();

Investor investor1 = new Investor("John");
Investor investor2 = new Investor("Jane");

techStock.Attach(investor1);
techStock.Attach(investor2);

techStock.SetPrice(150.50);
// Outputs:
// John received an update: Stock price is now $150.50
// Jane received an update: Stock price is now $150.50

techStock.Detach(investor1);

techStock.SetPrice(155.25);
// Outputs:
// Jane received an update: Stock price is now $155.25
}
}


The Command Design Pattern

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
using System;

// Command interface
public interface ICommand
{
void Execute();
}

// Concrete Command classes
public class LightOnCommand : ICommand
{
private Light light;

public LightOnCommand(Light light)
{
this.light = light;
}

public void Execute()
{
light.TurnOn();
}
}

public class LightOffCommand : ICommand
{
private Light light;

public LightOffCommand(Light light)
{
this.light = light;
}

public void Execute()
{
light.TurnOff();
}
}

// Receiver class
public class Light
{
public void TurnOn()
{
Console.WriteLine("Light is ON");
}

public void TurnOff()
{
Console.WriteLine("Light is OFF");
}
}

// Invoker class
public class RemoteControl
{
private ICommand command;

public void SetCommand(ICommand command)
{
this.command = command;
}

public void PressButton()
{
command.Execute();
}
}

// Usage
class Program
{
static void Main()
{
Light livingRoomLight = new Light();

ICommand lightOn = new LightOnCommand(livingRoomLight);
ICommand lightOff = new LightOffCommand(livingRoomLight);

RemoteControl remote = new RemoteControl();

remote.SetCommand(lightOn);
remote.PressButton(); // Outputs: Light is ON

remote.SetCommand(lightOff);
remote.PressButton(); // Outputs: Light is OFF
}
}


Object Pool Design Pattern

Create a pool of instantiated objects, and use them as needed without destroying them, just return them to the pool.



State Design Pattern

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// State interface
public interface ITrafficSignalState
{
void Handle();
}

// Concrete State classes
public class RedLightState : ITrafficSignalState
{
public void Handle()
{
Console.WriteLine("Traffic signal: RED - Stop!");
}
}

public class GreenLightState : ITrafficSignalState
{
public void Handle()
{
Console.WriteLine("Traffic signal: GREEN - Go!");
}
}

public class YellowLightState : ITrafficSignalState
{
public void Handle()
{
Console.WriteLine
("Traffic signal: YELLOW - Prepare to stop.");
}
}

// Context class
public class TrafficSignal
{
private ITrafficSignalState currentState;

public TrafficSignal(ITrafficSignalState initialState)
{
currentState = initialState;
}

public void ChangeState(ITrafficSignalState newState)
{
currentState = newState;
}

public void Request()
{
currentState.Handle();
}
}

// Usage
class Program
{
static void Main()
{
TrafficSignal
trafficSignal = new TrafficSignal(new RedLightState());

trafficSignal.Request();
// Outputs: Traffic signal: RED - Stop!

trafficSignal.ChangeState(new GreenLightState());
trafficSignal.Request();
// Outputs: Traffic signal: GREEN - Go!
}
}


The Strategy Design Pattern

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// Strategy interface
public interface IAbilityStrategy
{
void Execute();
}

// Concrete Strategy classes
public class AttackStrategy : IAbilityStrategy
{
public void Execute()
{
Console.WriteLine("Character attacks!");
}
}

public class HealStrategy : IAbilityStrategy
{
public void Execute()
{
Console.WriteLine("Character heals!");
}
}

// Context class
public class Character
{
private IAbilityStrategy ability;

public Character(IAbilityStrategy initialAbility)
{
ability = initialAbility;
}

public void SetAbility(IAbilityStrategy newAbility)
{
ability = newAbility;
}

public void UseAbility()
{
ability.Execute();
}
}

// Usage
class Program
{
static void Main()
{
Character warrior = new Character(new AttackStrategy());
Character healer = new Character(new HealStrategy());

warrior.UseAbility();
// Outputs: Character attacks!
healer.UseAbility();
// Outputs: Character heals!

// Switching abilities dynamically
warrior.SetAbility(new HealStrategy());
warrior.UseAbility();
// Outputs: Character heals!
}
}


Notes

Universal RP

这个在unity 3D universal中自带,但3D core中没有.
导致部分material会出现显示错误(pink).

可以把material给成standard或者增添这个package.



Computer Networks

Latency and Lag

Reducing the delay between a player’s action and the game’s response is critical to maintaining smooth gameplay.
High latencies can lead to frustrating lags and negatively impact the gaming experience.

Server Architecture

Understanding server models, such as peer-to-peer and client-server architectures, is essential in deciding how players connect and interact with each other and the game environment.

Bandwidth Optimization

Multiplayer games constantly exchange data between players and servers.
Efficiently managing and optimizing bandwidth usage is crucial to ensure a responsive and stable gaming experience.

Network Security

Implementing robust security measures is vital to protect the integrity of the game, prevent cheating, and safeguard players’ personal information.



UDP (User Datagram Protocol)

UDP is commonly used in multiplayer games due to its low overhead and real-time data delivery, making it suitable for fast-paced gameplay.
However, it lacks error-checking and reliability, so developers must handle packet loss and data integrity themselves.

TCP (Transmission Control Protocol)

TCP provides reliable data transmission, ensuring that all packets reach their destination without errors.
Although it introduces slightly more latency compared to UDP, TCP is ideal for game features that demand accuracy and consistency.

Network Libraries and Middleware

Developers often utilize networking libraries and middleware that provide pre-built solutions for multiplayer functionality, saving time and effort in implementing complex networking features.



Structure of an IPv4 Address

An IPv4 address consists of a 32-bit binary number, represented in decimal format as four groups of digits separated by periods (dots).
Each group is called an octet, and it can hold values from 0 to 255.



Primary purpose:

Host Identification:

Each device, such as a computer, smartphone, router, or printer, connected to a network requires a unique IPv4 address.
This address serves as an identifier for the device, allowing data packets to be sent to and received from the correct destination.

Routing Data:

When data is transmitted over a network, it is broken down into packets. These packets contain the source and destination IPv4 addresses.
Routers use this information to forward packets through various network segments until they reach their intended destination.



Routing

Routing is a critical process that determines how data packets are forwarded from a source device to a destination device across an interconnected network.
When data is transmitted from one device to another, it is broken down into smaller units called data packets.
These packets travel through various network devices, such as routers, switches, and gateways, based on routing decisions to reach their intended destination.

Routing relies on network protocols and algorithms to efficiently direct data packets along the optimal path to their destination.
Routers play a significant role in this process as they examine the destination IP address of each packet and make decisions on where to send it next.
They maintain routing tables that contain information about the network’s topology, allowing them to determine the best path for forwarding data.



Types of Routing

Static Routing:

In static routing, network administrators manually configure the routing tables on routers.
This approach is suitable for small, stable networks with predictable traffic patterns.

Dynamic Routing:

Dynamic routing protocols enable routers to exchange information with neighboring routers, allowing them to adapt and update their routing tables automatically.
This flexibility is ideal for larger networks with dynamic changes in network topology.

Default Routing:

Default routes are used when a router cannot find an explicit route for a packet.
They act as a “last resort” and direct packets to a predefined gateway or next hop.



Transport Protocols

Transport protocols manage the delivery of data packets between source and destination devices.
Two primary transport protocols used in computer networks are:

Transmission Control Protocol (TCP):
TCP is a connection-oriented protocol that ensures reliable and ordered data delivery.
It establishes a virtual connection between the sender and receiver before data transmission, and acknowledgments are used to confirm successful packet delivery.
If a packet is lost, TCP will request retransmission to maintain data integrity.

User Datagram Protocol (UDP):
UDP is a connectionless protocol that offers minimal overhead, making it suitable for real-time applications like video streaming and online gaming.
Unlike TCP, UDP does not guarantee reliable data delivery or maintain the order of packets.
However, its lower latency makes it ideal for time-sensitive applications.



Client-Server Architecture

Client-server architecture is a widely used model in computer networking, where devices are categorized into two main roles: clients and servers.
In this model, clients request services or resources from servers, and servers provide these services in response to client requests.
This architecture allows for scalable and efficient communication between devices over a network.

Clients

Clients are the end-user devices, such as computers, smartphones, or gaming consoles, that interact with the application or service.
They initiate requests for data or actions, and upon receiving the responses from servers, they display the results to users.
Clients are responsible for providing the user interface and handling user inputs.

Servers

Servers are powerful computers or devices with high processing capabilities and ample storage.
They are responsible for hosting applications, services, or resources that clients access.
Servers process client requests, perform computations, store data, and send the results back to the clients.



Client-Server Architecture in Multiplayer Games

In the context of multiplayer games, the client-server architecture is crucial for enabling real-time interactions between players.
Here’s how it works:



Game Servers:

In multiplayer games, dedicated game servers act as the authoritative source of game state.
These servers manage the game environment, including player positions, actions, and interactions with the game world.
Each player’s client communicates with the game server to exchange information about the game, including their actions and the actions of other players.

Client Roles:

Players’ devices (clients) connect to the game server over the network.
Each client renders the game locally on the player’s screen, handling graphics, user inputs, and local game logic.
However, critical game decisions and state information are maintained on the game server to prevent cheating and ensure consistency among all players.

Player Interactions:

When a player performs an action in the game, such as moving, shooting, or picking up an item, the client sends a request to the game server.
The server processes the action, validates it for fairness, and updates the game state accordingly.

Real-Time Updates:

The game server continuously sends updates to all connected clients, providing real-time information about the game world.
These updates include the positions and actions of other players, ensuring that every player experiences the same game environment.

Latency and Lag:

Reducing latency (the delay between sending a request and receiving a response) is crucial in multiplayer games to provide a smooth and immersive experience.
Game developers and network engineers employ various techniques to minimize lag and optimize the responsiveness of the game.

The client-server architecture offers seamless real-time interactions among players, allowing them to compete, collaborate, and immerse themselves in a shared gaming experience. This model ensures that the game environment remains fair, secure, and consistent across all players, contributing to the success and enjoyment of the multiplayer gaming experience.



Client-Server Interaction:

In an RPC setup, one device acts as the client, initiating the procedure call, while the other device functions as the server, executing the procedure requested by the client.

Marshalling and Unmarshalling:

Before transmitting the RPC request, the client’s parameters and function call are converted into a format suitable for network transmission (marshalling).
The server receives the request and converts the data back to its original format (unmarshalling) to execute the function.

Remote Procedure Execution:

The server processes the procedure call and executes the requested function using its local resources.
The results (if any) are then sent back to the client through the network.



Here’s how RPC is used in multiplayer games:

Player Actions and Synchronization:

When a player performs an action, such as shooting or moving, the client sends an RPC request to the game server, notifying it of the action taken.

Game State Updates:

The server processes these RPC requests and updates the game state accordingly.
For example, if a player moves to a new location, the server updates the player’s position in the game world.

Broadcasting Events:

RPC allows the server to broadcast important events, such as player deaths, to all connected clients.
This ensures that all players are aware of critical changes in the game environment.

Authority and Anti-Cheating Measures:

RPC helps enforce authority in multiplayer games by allowing the server to validate and execute crucial game logic.
This prevents clients from manipulating data and cheating in the game.

Minimizing Bandwidth Usage:

RPCs can be designed to send only relevant data to clients, reducing the amount of information transmitted over the network.
This optimization is essential for maintaining smooth gameplay and reducing lag.



Decentralized Game Logic:

In this model, clients maintain their version of the game state and continuously send their actions to the game server.
The server receives these actions but does not actively validate or enforce them immediately.

Local Responsiveness:

Client Authoritative games offer low latency and immediate feedback to players, as the game logic is executed locally on each client.
Players experience minimal input delay and a smooth, responsive gaming experience.

Risk of Cheating:

Since clients have some authority over game logic and actions, there is a higher risk of cheating and exploiting the game.
Malicious players can manipulate their client-side data or create hacks to gain an unfair advantage.



Centralized Game Logic:

In this model, the game server maintains the authoritative game state, and clients send their actions to the server for validation and execution.

Prevents Cheating:

Server Authoritative games offer better security against cheating since the server actively validates and enforces all actions.
Players cannot manipulate game logic on their clients to gain unfair advantages.

Increased Latency:

As all actions must be validated by the server, Server Authoritative games might introduce higher latency, especially if players are geographically far from the game server.





Data Packets

Data packets are the basic units of information sent across a network.
When data is transmitted over a network, it is divided into smaller, manageable pieces called data packets.
Each packet contains a header and payload.
The header includes crucial information, such as the source and destination IP addresses, packet sequence number, and other control data.
The payload contains the actual data being transmitted.



RESP API

REST (Representational State Transfer) APIs are a set of conventions and guidelines that facilitate the communication and interaction between different software systems over the internet.
RESTful APIs use standard HTTP methods to perform various operations on resources, allowing clients to access and manipulate data on a server in a structured and organized manner.

API Methods

API methods define the types of actions that can be performed on resources using the RESTful API.
The four primary HTTP methods used in RESTful APIs are:

GET:
The GET method is used to retrieve data from the server.
It requests information about a resource and does not modify or alter it.

POST:
The POST method is used to create new resources on the server.
It submits data to the server to be processed and stored.

PUT:
The PUT method is used to update existing resources on the server.
It replaces the current state of a resource with the new data provided in the request.

DELETE:
The DELETE method is used to remove a resource from the server.
It instructs the server to delete the specified resource.



Terminology

Request:
A request is an HTTP message sent by the client to the server, containing information about the desired operation and any necessary data.

Response:
A response is an HTTP message sent by the server to the client in reply to a request.
It contains the requested data or the status of the requested operation.

Request or Response Headers:
Headers are additional information sent along with the request or response.
They include metadata about the data being transmitted, authentication credentials, content types, etc.

Request Body:
The request body is the data sent with a POST or PUT request.
It contains the payload or content that needs to be processed by the server.

Form-data:
Form-data is a way to send data in key-value pairs, usually associated with HTML forms.
It is commonly used in POST requests to submit form data.

Raw Data (JSON, XML, Text, etc.):
In addition to form-data, REST APIs can accept data in various formats like JSON, XML, and plain text.
JSON (JavaScript Object Notation) and XML (eXtensible Markup Language) are widely used for exchanging structured data.

Response Codes:
Response codes, also known as HTTP status codes, indicate the result of a request.

Common response codes include:

200 OK: Request was successful.
201 Created: Resource was successfully created.
204 No Content: Request was successful, but there is no data to return.
400 Bad Request: The server cannot understand the request.
401 Unauthorized: Authentication failed or user lacks permission.
404 Not Found: The requested resource could not be found.
500 Internal Server Error: An error occurred on the server-side.



Data Structure

一些过去的回忆.就只贴点图了.



Recursive factorial function.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int Factorial(int n)
{
// Base case: factorial of 0 is 1
if (n == 0)
return 1;

// Recursive case: multiply n by factorial of (n - 1)
return n * Factorial(n - 1);
}

// Example usage
int result = Factorial(5);

// Output: 120
Console.WriteLine(result);


Dijkstra’s Algorithm.

One of the most common algorithms used for finding the shortest path in graphs is Dijkstra’s algorithm.
Here’s a simplified explanation of how it works:

Start by assigning a tentative distance value to every vertex in the graph.
Set the distance of the source vertex to 0 and all other vertices to infinity.
Mark the source vertex as the current vertex.
For the current vertex, consider all of its unvisited neighbors and calculate their tentative distances by summing the distance of the current vertex and the weight of the edge between them.
If the calculated tentative distance of a neighbor is less than its current assigned distance, update the neighbor’s distance value.
Once all the neighbors of the current vertex have been considered, mark the current vertex as visited and select the unvisited vertex with the smallest tentative distance as the next current vertex.
Repeat steps 3-5 until the destination vertex is marked as visited or there are no more unvisited vertices.
The shortest path can then be reconstructed by backtracking from the destination vertex to the source vertex using the recorded distances and visited vertices.
Dijkstra’s algorithm guarantees that the shortest path is found, given that the graph doesn’t contain negative weight cycles.
It explores vertices in a greedy manner, always selecting the vertex with the smallest tentative distance as the next current vertex.



Quick Sort implementation in C#.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// Function to perform Quick Sort
void QuickSort(int[] array, int low, int high)
{
if (low < high)
{
// Partition the array and get the pivot index
int pivotIndex = Partition(array, low, high);

// Recursively sort the subarrays before and after the pivot
QuickSort(array, low, pivotIndex - 1);
QuickSort(array, pivotIndex + 1, high);
}
}

// Function to partition the array and find the pivot index
int Partition(int[] array, int low, int high)
{
int pivot = array[high]; // Choose the last element as the pivot
int i = low - 1;

for (int j = low; j < high; j++)
{
if (array[j] < pivot)
{
i++;
// Swap elements at indices i and j
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}

// Place the pivot in its correct position
int pivotIndex = i + 1;
int temp2 = array[pivotIndex];
array[pivotIndex] = array[high];
array[high] = temp2;

return pivotIndex;
}

// Example usage
int[] numbers = { 5, 2, 9, 1, 3 };
QuickSort(numbers, 0, numbers.Length - 1);

// Output: 1, 2, 3, 5, 9
Console.WriteLine(string.Join(", ", numbers));


What is the difference between a layer and a tag?

Layers are used to isolate things in the engine. Tags are used to label or categorize things.



Sketchpad

Windows -> package manager -> download packages的时候.
如果使用git from URL会默认到最新版本,比search更新一些.