# Space and Motion — MVC Architecture Learning

Time：2022-11-13

## Space and Motion — MVC Architecture Learning

### Short answer and program verification [recommended]

• What is the nature of game object movement?
• Please use more than three methods to realize the parabolic motion of the object. (For example, modify the Transform property, use the vector Vector3 method…)
• Write a program to realize a complete solar system. The rotation speed of other planets around the sun must be different and not on the same normal plane.

• The essence of movement is changetransformof the three properties (Position, Rotation, Scale) value to make the object move

There are three ways to achieve parabolic motion:

• The method of changing the position of the object, because we want to achieve parabolic motion, we know that in the interface of unity, the change of the vertical direction is to change the y-axis, according to the formula of the parabola $y=v_0t+\frac{1}{2}at^ 2$, the parabola is moving downward, you can add a minus sign to the formula to realize the desired movement of the object

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class NewBehaviourScript : MonoBehaviour
{
public float vx = 0.5f; // velocity in the x-axis direction
public float v0 = 0; // initial velocity in the y-axis direction
const float a = 9.8f; //acceleration a
// Start is called before the first frame update
void Start()
{
}

// Update is called once per frame
void Update()
{
this.transform.position += new Vector3(vx * Time.deltaTime, (float)(-v0 * Time.deltaTime - 0.5 * a * (Time.deltaTime) * (Time.deltaTime)), 0);
v0 += a * Time.deltaTime; // The speed of v0 is different when each frame changes
}
}
• Use the translate function of transform, this function makes the position of the object move to the corresponding position, the specific formula is the parabola formula: $y=v_0t+\frac{1}{2}at^2$
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
public float vx = 0.5f; // velocity in the x-axis direction
public float v0 = 0; // initial velocity in the y-axis direction
const float a = 9.8f; //acceleration a
// Start is called before the first frame update
void Start()
{

}

// Update is called once per frame
void Update()
{
this.transform.Translate(new Vector3(vx * Time.deltaTime, (float)(-v0 * Time.deltaTime - 0.5 * a * (Time.deltaTime) * (Time.deltaTime)), 0));
v0 += a * Time.deltaTime;
}
}
• A function can be called in transformSetPositionAndRotation, this function can set the position and rotation of the object. The rotation does not need to be set. The value of the position setting is basically the same as the above, but at this time, it is necessary to additionally set the time change, and the height of the initial object is also the y value.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class NewBehaviourScript : MonoBehaviour
{
public float vx = 0.5f; // velocity in the x-axis direction
const float a = 9.8f; //acceleration a
public float t = 0;
public float y = 10.0f; // initial y position
// Start is called before the first frame update
void Start()
{

}

// Update is called once per frame
void Update()
{
this.transform.SetPositionAndRotation(new Vector3(vx * t, y - (float)(0.5 * a * t * t), 10), this.transform.rotation);
t += Time.deltaTime;
}
}
• To make the solar system, first drag the pictures of all planets to the desktop from the solar system map given by the teacher, and then drag them into Unity to use them in assets, and then drag these pictures to the stars of different sizes. planets can be formed

According to the code given by the teacher in class, make certain modifications to complete the scene of the planets of the entire solar system rotating around the sun

It can also be seen that several planets are not on the same normal plane, so the requirements are fulfilled

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Rotate : MonoBehaviour
{
public int v; // speed of rotation
public Vector3 e;
float x, y; // normal vector
// Start is called before the first frame update
void Start()
{
// The generated normal vector should be random due to the different normal planes
x = Random.Range(1, 10); y = Random.Range(10, 20);
e = new Vector3(x, y, 0);
}
// Update is called once per frame
void Update()
{
Quaternion q = Quaternion.AngleAxis(v * Time.deltaTime, e);
this.transform.localPosition = q * this.transform.localPosition;
}
}

The normal vector of the above code is randomly generated, so it is not in the same normal plane. The speed can be set by yourself, so the speed of the planet’s rotation around the sun can be different.

### programming practice

• Read the game script below

Priests and Devils

Priests and Devils is a puzzle game in which you will help the Priests and Devils to cross the river within the time limit. There are 3 priests and 3 devils at one side of the river. They all want to get to the other side of this river, but there is only one boat and this boat can only carry two persons each time. And there must be one person steering the boat from one side to the other side. In the flash game, you can click on them to move them and click the go button to move the boat to the other direction. If the priests are out numbered by the devils on either side of the river, they get killed and the game is over. You can try it in many > ways. Keep all priests alive! Good luck!

Requirements the program needs to meet:

• play the game ( http://www.flash-game.net/gam… )
• List the things mentioned in the game (Objects)
• Use a table to list the player action table (rule table), note that the fewer actions the better
• Please make in-game objects a prefab
• in scene controllerLoadResourcesThe method loads and initializes rectangles, squares, balls and their colors representing objects in the game.
• Use C# Collection Types to Organize Objects Efficiently
• The whole game only has the main camera and one Empty object, other objects must be dynamically generated by code! ! . The entire game is not allowed to appear Find game objects, SendMessage and other communication coupling statements that break through the program structure. Violation of this rule, no points will be awarded
• Please use the courseware architecture diagram for programming, non-MVC structure programs are not accepted
• Pay attention to details, such as: the ship has not docked, the priest and the devil are in the movement of getting on and off the boat, and user events cannot be accepted!

#### game rules

Priest and Devil: This is a classic game, the game is very simple, the player needs to control the ship, the priest and the devil, and then make the three priests and three devils on one shore move to the other shore. And it needs to be operated within 60 seconds of the time limited by the game.

Notice

• For the boat to move, there must be a priest or a devil on it
• Maximum of two characters on board
• No matter which shore, as long as the number of devils is greater than the number of priests, the game fails (the number includes the number of characters on board when the ship is docked)

#### Game Screenshots and Videos

Because the game process is relatively long, only part of the process map is intercepted, and the specific game process is in the video link

#### Game Assets Structure

The designed structure is completely built according to the standard structure, as shown in the following figure

• Materials: Store the in-game Material
• Resources: Prefabs that hold objects in the game
• Scenes: The scene where the game is stored
• Scripts: store the c# code of the game
• Texture: store some pictures about the object

#### things in the game

• Water: Water, that is, the pastor and the devil need to pass through, is composed of a cuboid, and hangs pictures found on the Internet
• Devil: Devil, consisting of a black cuboid
• Priest: Priest, consisting of a green cuboid
• ground: two shores, composed of cuboids, with pictures on them
• Boat: A wooden boat, consisting of a cuboid, with pictures on it

#### MVC structure programming

Before programming, you need to knowMVCthe general structure of

MVC is an architectural pattern for interface human-computer interaction programming. It divides the program into three parts:

• Model: Data objects and relationships

• Game Objects, Spatial Relationships
• Controller (Controller): accept user events and control model changes

• One scene one master controller
• Implement at least the interface that interacts with the player (IPlayerAction)
• Implement or manage movement
• Interface (View): Display the model and hand over human-computer interaction events to the controller for processing

• Render the GUI, receive events

This experiment is also written in strict accordance with the MVC structure. The general composition of the code is shown in the following figure:

The variables in the class are set to different types due to different permissions, public, private,readonly(a read-only variable cannot modify its value)

The following describes the specific functions implemented by each code:

• Director: The controller belonging to the highest level, there is always an instance at runtime, which facilitates the communication between classes.

The main responsibilities are:

• Get the scene of the current game
• Control scene running, switching, stacking and popping
• Pause, Resume, Exit
• Manage the global state of the game
• Set game configuration
• Set the game global view

Controllers of different scenes can be accessed through an abstract scene interface

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// responsible for instantiation
public class Director : System.Object {
private static Director _instance;
public SceneController currentSceneController { get; set; }

public static Director getInstance() {
if (_instance == null)  _instance = new Director ();
return _instance;
}
}
• SceneController: Scene Controller:

Responsibilities:

• Manage all game objects in this scene
• Coordinate communication between game objects (prefab level)
• Respond to external input events
• Rules governing the match (referee)
• various chores

It can be seen that it is implemented by interface, indicating that objects cannot be created directly. We can know from the previous study that the use of interface requires a class to inherit it, so that it can be used.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// Scene management: load all resources
public interface SceneController {
}
• RoleController: You can know from the name of the class, this is the controller of the game character, it can control the movement of the character, and can get the relevant information of the character, such as whether the game character is a priest or a devil, on a ship or on the shore, the game character What is the name and so on. Not only the above functions are needed, but since the characters can be manipulated, we also need to monitor the click function of the mouse on the characters.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// controller for the game character
public class RoleController {
readonly int PorD; // judge whether it is a priest (0) or a devil (1)

bool _isOnBoat;
GroundController gController;

public RoleController(string r) {

if (r == "priest") {
obj = Object.Instantiate (Resources.Load ("Perfabs/Priest", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
PorD = 0;
} else {
obj = Object.Instantiate (Resources.Load ("Perfabs/Devil", typeof(GameObject)), Vector3.zero, Quaternion.identity, null) as GameObject;
PorD = 1;
}
mov = obj.AddComponent (typeof(Moving)) as Moving;

clickGUI = obj.AddComponent (typeof(ClickGUI)) as ClickGUI;
clickGUI.setController (this);
}

public void setName(string n) {
obj.name = n;
}

public void setPosition(Vector3 p) {
obj.transform.position = p;
}

public void Movingto(Vector3 dest) {
mov.setDestination(dest);
}

public int getRole() {
return PorD;
}

public string getName() {
return obj.name;
}

public void getOnBoat(BoatController b) {
gController = null;
obj.transform.parent = b.getGameobj().transform;
_isOnBoat = true;
}

public void getGround(GroundController coastCtrl) {
gController = coastCtrl;
obj.transform.parent = null;
_isOnBoat = false;
}

public bool isOnBoat() {
return _isOnBoat;
}

public GroundController getGroundController() {
return gController;
}

public void reset() {
mov.reset ();
gController = (Director.getInstance ().currentSceneController as FirstController).g1;
getGround (gController);
setPosition (gController.getEmptyPosition ());
gController.getGround (this);
}
}
• GroundController: controller on ground, two read-only variablessPosition、ePositionrecorded the positions of the two grounds, thenposThe array records the positions of storable characters on the land; the modification of variables by the actions of landing and landing; before landing on the land, it is necessary to judge the index of the empty position on the land, and then the content of the variable can be modified
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// controller on land
public class GroundController {
readonly Vector3 sPosition = new Vector3(9, 1, 0);
readonly Vector3 ePosition = new Vector3(-9, 1, 0);

RoleController[] roles;

public GroundController(string ss) {
pos = new Vector3[] {new Vector3(6.5F,2.25F,0), new Vector3(7.5F,2.25F,0), new Vector3(8.5F,2.25F,0),
new Vector3(9.5F,2.25F,0), new Vector3(10.5F,2.25F,0), new Vector3(11.5F,2.25F,0)};

roles = new RoleController[6];

if (ss == "from") {
ground = Object.Instantiate (Resources.Load ("Perfabs/Ground", typeof(GameObject)), sPosition, Quaternion.identity, null) as GameObject;
ground.name = "from";
st_pos = 1;
} else {
ground = Object.Instantiate (Resources.Load ("Perfabs/Ground", typeof(GameObject)), ePosition, Quaternion.identity, null) as GameObject;
ground.name = "to";
st_pos = -1;
}
}

public int getEmptyIndex() {
for (int i = 0; i < roles.Length; i++) {
if (roles [i] == null) return i;
}
return -1;
}

public Vector3 getEmptyPosition() {
Vector3 p = pos [getEmptyIndex ()];
p.x *= st_pos;
return p;
}

public void getGround(RoleController r) {
int ii = getEmptyIndex ();
roles [ii] = r;
}

public RoleController getOffGround(string pname) {    // 0->priest, 1->devil
for (int i = 0; i < roles.Length; i++) {
if (roles [i] != null && roles [i].getName () == pname) {
RoleController r = roles [i];
roles [i] = null;
return r;
}
}
return null;
}

public int get_st_pos() {
return st_pos;
}

public int[] getRoleNum() {
int[] cnt = {0, 0};
for (int i = 0; i < roles.Length; i++) {
if (roles [i] == null) continue;

if (roles [i].getRole () == 0) cnt[0]++;
else cnt[1]++;
}
return cnt;
}

public void reset() {
roles = new RoleController[6];
}
}
• BoatController: The controller for the ship, with two read-only variablessPosition,ePosition, to represent the start and end positions of the boat, and pass the variablest_posTo judge whether it is the starting point or the ending point, it is also necessary to control the movement of the ship, so it is necessary to monitor the click function of the mouse. It is also necessary to judge whether the ship is empty, the index corresponding to the empty position, and the notification of actions such as boarding and disembarking; the implementation of this class is similar to the implementation of the above land controller, so the design is relatively simple.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// ship's controller
public class BoatController {
readonly Vector3 sPosition = new Vector3 (5, 1, 0); // starting position
readonly Vector3 ePosition = new Vector3 (-5, 1, 0); // reach position

int st_pos; // start point or end point: -1: end point 1: start point
RoleController[] member = new RoleController[2];

public BoatController() {
st_pos = 1;

sPositions = new Vector3[] { new Vector3 (4.5F, 1.5F, 0), new Vector3 (5.5F, 1.5F, 0) };
ePositions = new Vector3[] { new Vector3 (-5.5F, 1.5F, 0), new Vector3 (-4.5F, 1.5F, 0) };

boat = Object.Instantiate (Resources.Load ("Perfabs/Boat", typeof(GameObject)), sPosition, Quaternion.identity, null) as GameObject;
boat.name = "boat";

mov = boat.AddComponent (typeof(Moving)) as Moving;
}

public void Move() {
if (st_pos == -1) {
mov.setDestination(sPosition);
st_pos = 1;
} else {
mov.setDestination(ePosition);
st_pos = -1;
}
}

public int getEmptyIndex() {
for (int i = 0; i < member.Length; i++) {
if (member [i] == null) return i;
}
return -1;
}

public bool isEmpty() {
for (int i = 0; i < member.Length; i++) {
if (member [i] != null) return false;
}
return true;
}

public Vector3 getEmptyPosition() {
Vector3 p;
int ii = getEmptyIndex ();
if (st_pos == -1) p = ePositions[ii];
else p = sPositions[ii];

return p;
}

public void GetOnBoat(RoleController r) {
int ii = getEmptyIndex ();
member [ii] = r;
}

public RoleController GetOffBoat(string member_name) {
for (int i = 0; i < member.Length; i++) {
if (member [i] != null && member [i].getName () == member_name) {
RoleController r = member [i];
member [i] = null;
return r;
}
}
return null;
}

public GameObject getGameobj() {
return boat;
}

public int get_st_pos() {
return st_pos;
}

public int[] getRoleNum() {
int[] cnt = {0, 0};
for (int i = 0; i < member.Length; i++) {
if (member [i] == null) continue;

if (member [i].getRole () == 0) cnt[0]++;
else cnt[1]++;
}
return cnt;
}

public void reset() {
mov.reset ();
if (st_pos == -1) Move ();

member = new RoleController[2];
}
}
• Moving: is a class about object movement, this class can control the speed of movement of the objectspeed, you can also set the object’s destination so that the object can move. Since the characters on the shore need to go through two steps to reach the boat, otherwise they may wear the mold, that is, the characters will pass through the surface of the ground, which will not be rigorous, so I setcurvariable to determine the position where the current object is running, and then set the corresponding destination according to the position (mid, dest) to solve such problems.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// Implementation of object movement
public class Moving: MonoBehaviour {

int cur; // current running position
Vector3 dest, mid; // set a mid position so that the motion doesn't wear out

public void setDestination(Vector3 d) {
dest = d; mid = d;

if (d.y == transform.position.y) cur = 2;
else if (d.y < transform.position.y) mid.y = transform.position.y;
else mid.x = transform.position.x;

cur = 1;
}

public void reset() {
cur = 0;
}

void Update() {
if (cur == 1) {
transform.position = Vector3.MoveTowards (transform.position, mid, speed * Time.deltaTime);
if (transform.position == mid) cur = 2;
} else if (cur == 2) {
transform.position = Vector3.MoveTowards (transform.position, dest, speed * Time.deltaTime);
if (transform.position == dest) cur = 0;
}
}
}
• FirstController: A higher-level controller that controls all objects in this scene, including its loading, communication, and user input.

inheritSceneControllerandUserAction, indicating that the controller implements the inheritance and implementation of the two interfaces; the controller also implements loading game resources and loading game characters, and controls the movement of characters and ships; the control of the running time of the game; to determine whether the game is End, and the conditions for the end of the game, if the game is over, reset the game and reset each variable

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// master controller
public class FirstController : MonoBehaviour, SceneController, UserAction {

readonly Vector3 p_water = new Vector3(0,0.5F,0); // water position

UserGUI uGUI;

public GroundController g1;
public GroundController g2;
public BoatController boat;
private RoleController[] roles;
private float time; // game running time

void Awake() {
Director d = Director.getInstance ();
d.currentSceneController = this;
uGUI = gameObject.AddComponent <UserGUI>() as UserGUI;
roles = new RoleController[6];
time = 60;
}

// run the game time
void Update() {
time -= Time.deltaTime;
this.gameObject.GetComponent<UserGUI>().time = (int) time;
uGUI.isWin = isfinished ();
}

for (int i = 0; i < 3; i++) {
RoleController r = new RoleController ("priest");
r.setName("priest" + i);
r.setPosition (g1.getEmptyPosition ());
r.getGround (g1); g1.getGround (r);

roles [i] = r;
}

for (int i = 0; i < 3; i++) {
RoleController r = new RoleController ("devil");
r.setName("devil" + i);
r.setPosition (g1.getEmptyPosition ());
r.getGround (g1); g1.getGround (r);

roles [i+3] = r;
}
}

GameObject water = Instantiate (Resources.Load ("Perfabs/Water", typeof(GameObject)), p_water, Quaternion.identity, null) as GameObject;
water.name = "water";

g1 = new GroundController ("from");
g2 = new GroundController ("to");
boat = new BoatController ();

}

public void MoveBoat() {
if (boat.isEmpty ()) return;
boat.Move ();
uGUI.isWin = isfinished ();
}

public void MoveRole(RoleController r) {
if (r.isOnBoat ()) {
GroundController which_g;
if (boat.get_st_pos () == -1) which_g = g2;
else which_g = g1;

boat.GetOffBoat (r.getName());
r.Movingto (which_g.getEmptyPosition ());
r.getGround (which_g);
which_g.getGround (r);
} else {
GroundController which_g = r.getGroundController ();

if (boat.getEmptyIndex () == -1) return; // boat is empty
if (which_g.get_st_pos () != boat.get_st_pos ()) return;

which_g.getOffGround(r.getName());
r.Movingto (boat.getEmptyPosition());
r.getOnBoat (boat);
boat.GetOnBoat (r);
}
uGUI.isWin = isfinished ();
}
// Judge whether it is over 0: not over 1: lose 2: win
int isfinished() {
if (time < 0) return 1;
int p1 = 0; int d1 = 0; // number of priests and devils at the starting point
int p2 = 0; int d2 = 0; // number of priests and devils at the end

int[] cnt1 = g1.getRoleNum (); // the number of people at the starting point
p1 += cnt1[0]; d1 += cnt1[1];

int[] cnt2 = g2.getRoleNum (); // the number of people at the end point
p2 += cnt2[0]; d2 += cnt2[1];

if (p2 + d2 == 6) return 2;

int[] cnt3 = boat.getRoleNum (); // the number of people on the boat
if (boat.get_st_pos () == -1) {
p2 += cnt3[0]; d2 += cnt3[1];
} else {
p1 += cnt3[0]; d1 += cnt3[1];
}

if (p1 < d1 && p1 > 0) return 1;
if (p2 < d2 && p2 > 0) return 1;
return 0;
}

public void restart() {
time = 60;
boat.reset ();
g1.reset (); g2.reset ();
for (int i = 0; i < roles.Length; i++) roles [i].reset ();
}
}
• UserAction: Interface, which defines the operations that the player can performMoveBoat()moving ships,MoveRolemoving people,restartRestart the game. This interface is inherited by other classes, so that it can be implemented to get user input and then react
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

// Actions that the user or player can perform
public interface UserAction {
void MoveBoat();
void MoveRole(RoleController r);
void restart();
}
• UserGUI: User interaction, to set the entire user interface, determine whether the game is won, and display it if it winsWin, and a button to indicate if the game needs to be replayed; if it fails, it will showGameOver, then a game reset is also possible. Here I also set the font size and button size for easy viewing and aesthetics
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class UserGUI : MonoBehaviour {
private UserAction u;
public int isWin = 0;// 1:Gameover 2:Win
public int time; // game running time
GUIStyle ssize, buttons, tsize;

void Start() {
u = Director.getInstance ().currentSceneController as UserAction;

// set font size
ssize = new GUIStyle(); tsize = new GUIStyle();
ssize.fontSize = 45; tsize.fontSize = 20;
ssize.alignment = TextAnchor.MiddleCenter;

// set button size
buttons = new GUIStyle("button");
buttons.fontSize = 30;

// set the duration of the game
time = 60;
}
// Determine whether to win or lose, then reset
void OnGUI() {
GUI.Label(new Rect(0, 0, 100, 50), "Time:  " + time, tsize);
if (isWin == 1) {
GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height / 2 - 80, 100, 50), "Gameover!", ssize);
if (GUI.Button(new Rect(Screen.width / 2-65, Screen.height / 2, 140, 70), "Restart", buttons)) {
isWin = 0; u.restart ();
}
} else if(isWin == 2) {
GUI.Label(new Rect(Screen.width / 2 - 50, Screen.height / 2 - 80, 100, 50), " Win!", ssize);
if (GUI.Button(new Rect(Screen.width / 2 - 65, Screen.height / 2, 140, 70), "Restart", buttons)) {
isWin = 0; u.restart ();
}
}
}
}
• ClickGUI: Used to monitor the click function of the mouse, and callRoleControllerTake control of the character
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
// control for mouse click
public class ClickGUI : MonoBehaviour {
UserAction u;
RoleController roleController;

public void setController(RoleController rc) {
roleController = rc;
}

void Start() {
u = Director.getInstance ().currentSceneController as UserAction;
}

void OnMouseDown() {
if (gameObject.name == "boat") u.MoveBoat ();
else u.MoveRole (roleController);
}
}

### Thinking questions [optional]

• Using vectors and transformations, implement and extend the methods provided by Tranform, such as Rotate, RotateAround, etc.

• Rotate:

Rotates the object around the given axis by the number of degrees defined by the given angle.

Rotate has an axis, angle and the local or global parameters. The rotation axis can be in any direction.

It is necessary to realize the rotation angle x around the v axis. After getting the rotation, you can change the position and rotation of t

void Rotate(Transform t, Vector3 axis, float angle)
{
var r = Quaternion.AngleAxis(angle, axis);
t.position = r * t.position; t.rotation *= r;
}
• RotateAround:

Rotates the transform about axis passing through point in world coordinates by angle degrees.

This function rotates around the center. We need to get the displacement of the object. After the calculation is completed, rotate it and add the displacement of the center.

  void RotateAround(Transform t, Vector3 center, Vector3 axis, float angle)
{
var r = Quaternion.AngleAxis(angle, axis);
var dis = t.position - center;
dis *= r; t.position = center + dis; t.rotation *= r ;
}

## About Angular deployment and the relationship between the base hRef attribute in index.html

Run the command line directly under the SAP e-commerce cloud Spartacus UI projectng build, the output is as follows: dist folder: Put the dist folder undermystoredirectly into tomcatwebappsBelow the folder, when running: if modifiedbaseThe href attribute of the tab page: Then several JavaScript resource files in index.html cannot be loaded: Error: Error: Cannot get base […]