Skip to content

Commit 2fb2ba3

Browse files
committed
Added bytecode pattern
1 parent 4f163d6 commit 2fb2ba3

11 files changed

+534
-2
lines changed

Assets/Patterns/10. Bytecode.meta

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Patterns/10. Bytecode/Scripts.meta

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using System.Collections;
2+
using System.Collections.Generic;
3+
using UnityEngine;
4+
5+
namespace BytecodePattern
6+
{
7+
//Bytecode code pattern from the book "Game Programming Patterns"
8+
public class GameController : MonoBehaviour
9+
{
10+
11+
12+
void Start()
13+
{
14+
//Test
15+
int[] bytecode = new int[]
16+
{
17+
(int)Instruction.INST_LITERAL, 0, //wizard id
18+
(int)Instruction.INST_LITERAL, 75, //amount
19+
(int)Instruction.INST_SET_HEALTH
20+
};
21+
22+
VM vm = new VM(gameController: this);
23+
24+
vm.Interpret(bytecode);
25+
}
26+
27+
28+
void Update()
29+
{
30+
31+
}
32+
33+
34+
35+
//0 means the player's wizard and 1, 2, ... means the other wizards in the game
36+
//This way we can heal our own wizard while damage other wizards with the same method
37+
public void SetHealth(int wizardID, int amount)
38+
{
39+
Debug.Log($"Wizard {wizardID} gets health {amount}");
40+
}
41+
42+
public void SetWizdom(int wizardID, int amount)
43+
{
44+
Debug.Log($"Wizard {wizardID} gets wisdom {amount}");
45+
}
46+
47+
public void SetAgility(int wizardID, int amount)
48+
{
49+
Debug.Log($"Wizard {wizardID} gets agility {amount}");
50+
}
51+
52+
public void PlaySound(int soundID)
53+
{
54+
Debug.Log($"Play sound {soundID}");
55+
}
56+
57+
public void SpawnParticles(int particleType)
58+
{
59+
Debug.Log($"Spawn particle {particleType}");
60+
}
61+
62+
public int GetHealth(int wizardID)
63+
{
64+
return 50;
65+
}
66+
}
67+
}

Assets/Patterns/10. Bytecode/Scripts/GameController.cs.meta

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
using System.Collections;
2+
using System.Collections.Generic;
3+
using UnityEngine;
4+
5+
namespace BytecodePattern
6+
{
7+
//These are the instrusctions we can choose from in our programming language
8+
public enum Instruction
9+
{
10+
//Write stats
11+
INST_SET_HEALTH,
12+
INST_SET_WISDOM,
13+
INST_SET_AGILITY,
14+
INST_PLAY_SOUND,
15+
INST_SPAWN_PARTICLES,
16+
//So we can use parameters
17+
INST_LITERAL,
18+
//Read stats
19+
INST_GET_HEALTH,
20+
INST_GET_WISDOM,
21+
INST_GET_AGILITY,
22+
//Arithmetic
23+
INST_ADD
24+
}
25+
}

Assets/Patterns/10. Bytecode/Scripts/Instruction.cs.meta

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
using System.Collections;
2+
using System.Collections.Generic;
3+
using UnityEngine;
4+
5+
namespace BytecodePattern
6+
{
7+
public class VM
8+
{
9+
private GameController gameController;
10+
11+
//Will store values for later use in the switch statement
12+
private Stack<int> stackMachine = new Stack<int>();
13+
14+
//The max size of the stack
15+
private const int MAX_STACK = 128;
16+
17+
18+
19+
public VM(GameController gameController)
20+
{
21+
this.gameController = gameController;
22+
}
23+
24+
25+
26+
public void Interpret(int[] bytecode)
27+
{
28+
stackMachine.Clear();
29+
30+
//Read and execute the instructions
31+
for (int i = 0; i < bytecode.Length; i++)
32+
{
33+
//Convert from int to enum
34+
Instruction instruction = (Instruction)bytecode[i];
35+
36+
switch (instruction)
37+
{
38+
case Instruction.INST_SET_HEALTH:
39+
{
40+
//Important to pop amount before wizard because we push wizard before amount onto the stack
41+
int amount = Pop();
42+
int wizard = Pop();
43+
44+
gameController.SetHealth(wizard, amount);
45+
46+
break;
47+
}
48+
case Instruction.INST_LITERAL:
49+
{
50+
//Important that this i++ is not inside bytecode[i++] or it will not jump to next i
51+
i++;
52+
53+
int value = bytecode[i];
54+
55+
Push(value);
56+
57+
break;
58+
}
59+
case Instruction.INST_GET_HEALTH:
60+
{
61+
int wizard = Pop();
62+
63+
Push(gameController.GetHealth(wizard));
64+
65+
break;
66+
}
67+
case Instruction.INST_ADD:
68+
{
69+
int b = Pop();
70+
int a = Pop();
71+
72+
Push(a + b);
73+
74+
break;
75+
}
76+
default:
77+
{
78+
Debug.Log($"The VM couldn't find the instruction {instruction} :(");
79+
break;
80+
}
81+
}
82+
}
83+
}
84+
85+
86+
87+
//Stack methods
88+
private int Pop()
89+
{
90+
if (stackMachine.Count == 0)
91+
{
92+
Debug.LogError("The stack is empty :(");
93+
}
94+
95+
return stackMachine.Pop();
96+
}
97+
98+
private void Push(int number)
99+
{
100+
//Check for stack overflow, which is useful because someone might make a mod that tries to break your game
101+
if (stackMachine.Count + 1 > MAX_STACK)
102+
{
103+
Debug.LogError("Stack overflow is not just a place where you copy and paste code!");
104+
}
105+
106+
stackMachine.Push(number);
107+
}
108+
}
109+
}

Assets/Patterns/10. Bytecode/Scripts/VM.cs.meta

+11
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)