3D game (2)-the basis of discrete simulation engine


1. Short answer questions

Explain the difference and connection between game objects (GameObjects) and resources (Assets).

Difference: game objects usually appear directly in the scene, generally including players, enemies, environment and music, etc., which are the materialization of things with certain attributes and functions. Simply put, they assume part of the functions and carry them during the game. Components with certain attributes. The resources are materials prepared in advance by us, such as 3D models, audio files, images, scripts or any other types of files supported by Unity, which can be directly migrated and reused when creating games.

Connection: Game objects are the concrete manifestation of resource integration. Resources can be used by one or more game objects. Some resources are used as templates and can be instantiated into specific objects in the game. Game objects can also be saved as resources for reuse.

Download several game cases and summarize the structure of resources and object organizations (referring to the directory organization structure of resources and the hierarchical structure of the game object tree)

First, from a net of love download games source.

Insert picture description here

First of all, I downloaded a daily cool running game. This game was also popular for a long time.

Insert picture description here

The resource file directory includes Animations, Prefabs, and Scripts.

Insert picture description here

At the same time, the game objects are only the main camera (Main Camera), player (Player), background (BG), canvas (Canvas), event system (EventSystem), and game manager (GameManager). Because it's only a 2D game, it's almost the same after setting the player's actions and then changing the background and canvas.

Write a code that uses debug statements to verify the basic behavior of MonoBehaviour or the conditions triggered by the event

The basic behavior includes Awake() Start() Update() FixedUpdate() LateUpdate()

Common events include OnGUI() OnDisable() OnEnable()

    // Start is called before the first frame update
    void Start()
    {
        Debug.Log("Start");
    }

    // Update is called once per frame
    void Update()
    {
        Debug.Log("Update");
    }

    private void Awake()
    {
        Debug.Log("Awake");
    }

    private void FixedUpdate()
    {
        Debug.Log("FixedUpdate");
    }

    private void LateUpdate()
    {
        Debug.Log("LateUpdate");
    }

    private void OnGUI()
    {
        Debug.Log("OnGUI");
    }

    private void OnDisable()
    {
        Debug.Log("OnDisable");
    }

    private void OnEnable()
    {
        Debug.Log("OnEnable");
    }

operation result:

Insert picture description here

Find the script manual to learn about GameObject, Transform, Component objects

Translate the official description of the three objects respectively (Description)

GameObject: The base class of all entities in Unity Scenes.

Transform: The position, rotation and scale of the object. Every object in the scene has a transformation. It is used to store and manipulate the position, rotation and scale of objects. Each transform can have a parent, which allows you to apply position, rotation, and scaling hierarchically. This is the hierarchy seen in the "Hierarchy" pane. They also support enumerators.

Component: The base class of all components attached to GameObject.

Describe the properties of the table object (entity), the properties of the table's Transform, and the components of the table in the figure below

Insert picture description here

First, the property of the table object is GameObject.

The first selection box is the activeSelf attribute, the second is the object name, the third is the static attribute, the next line is the tag and layer, and the next line is the preset. Then there are Transform, Mesh Filter, Box Collider, Mesh Renderer.

In the Transform of the table object, Position is the spatial position, Rotation is the rotation angle, and Scale is the scale.

Finally, the table components are chair1, chair2, chair3, chair4.

Use UML diagrams to describe the relationship between the three

Insert picture description here

Resource presets (Prefabs) and object clones (clone)

What are the benefits of Prefabs?

You can repeatedly create game objects with the same structure. It is conducive to resource reuse, reduces workload, and is suitable for batch processing.

What is the relationship between preset and object clone (clone or copy or Instantiate of Unity Object)?

The objects generated in the preset are closely related to the preset objects. When the preset is modified, the resulting objects will also be modified, so it is suitable for batch processing.

In cloning, the parent and child are independent of each other. If the parent is modified, the child will not change. Similarly, if the child is modified, the parent will not change.

Make a table prefab, write a piece of code to instantiate the table prefab resource into a game object

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

public class instantiation: MonoBehaviour
{

    void Start () {
        GameObject table = Resources.Load("table") as GameObject;
        Instantiate(table);
        table.transform.position = new Vector3(0, 6, 0);
        table.transform.parent = this.transform;
	}
	
	void Update () {
		
	}
}

Insert picture description here

Mount the script to an empty object and run it.

Insert picture description here

2. Programming practice, mini games

Easy Tic-Tac-Toe game

Insert picture description here

For the ui interface, I didn't spend too much effort, it was completely built using IMGUI, just write a script and mount it on an empty object.

The variables that the script needs to define are as follows, save the board, the current round, the mode (player/AI), and move first. At the same time define an init function, used to initialize the board; check function, used to judge the current game state of the board; then there are PVP and PVA functions, which deal with two different modes respectively, the core idea is to read the player's operation information , Update the board and finally render the board one grid by grid; the difference between PVP and PVA is that PVP can directly read the operation information of two players, while PVA needs to call an AI function in the round of player 2 Let's choose the next operation; here, my AI algorithm uses the lowest method, if you can directly win, you will win directly. If the opponent can directly win, block the chess pieces. In other cases, the middle grid is selected according to the priority, and then the next Four corners, again choose the edge grid strategy to place the pieces.

The design of the above variables and functions has only one purpose, and that is to better implement the onGUI function. From the previous experiment, we can see that the onGUI function is called every once in a while, and the function is to render the UI every once in a while. Therefore, in the function, the overall framework is first made, and finally the PVP or PVA function is called according to two different modes.

The complete code is as follows:

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

public class NewBehaviourScript : MonoBehaviour
{
    private int[,] board=new int[3,3];
    private int turn=0;
    private int mode=0;
    private int initturn=0;

    void init(){
        turn =initturn;
        for(int i=0;i<3;i++){
            for(int j=0;j<3;j++){
                board[i,j]=0;
            }
        }
    }

    int check(){
        if(board[0,0]!=0&&board[0,0]==board[1,1]&&board[0,0]==board[2,2]) return board[0,0];
        if(board[2,0]!=0&&board[2,0]==board[1,1]&&board[2,0]==board[0,2]) return board[2,0];
        int cnt=0;
        for(int i=0;i<3;i++){
            if(board[i,0]!=0&&board[i,0]==board[i,1]&&board[i,0]==board[i,2]) return board[i,0];
            if(board[0,i]!=0&&board[0,i]==board[1,i]&&board[0,i]==board[2,i]) return board[0,i];
            for(int j=0;j<3;j++){
                if(board[i,j]==0) cnt++;
            }
        }
        return cnt==0? 3:0;
    }

    void PVP(){
        for(int i=0;i<3;i++){
            for(int j=0;j<3;j++){
                switch(board[i,j]){
                    case 0:
                        if(GUI.Button(new Rect(Screen.width/2-120+i*100,Screen.height/2-140+j*100,100,100)," ")&&check()==0){
                            board[i,j]=turn+1;
                            turn=1-turn;
                        }
                        break;
                    case 1:
                        GUI.Button(new Rect(Screen.width/2-120+i*100,Screen.height/2-140+j*100,100,100),"O");
                        break;
                    case 2:
                        GUI.Button(new Rect(Screen.width/2-120+i*100,Screen.height/2-140+j*100,100,100),"X");
                        break;
                }
            }
        }
    }

    void AI(){
        if(check()!=0) return;
        int cnt=0;
        int[] chose=new int[9];
        int[] prefer={0,(2<<2)+0,2,(2<<2)+2};
        for(int i=0;i<3;i++){
            for(int j=0;j<3;j++){
                if(board[i,j]==0){
                    board[i,j]=2;
                    if(check()==2){
                        return;
                    }
                    board[i,j]=1;
                    if(check()==1){
                        board[i,j]=2;
                        return;
                    }
                    board[i,j]=0;
                    chose[cnt++]=(i<<2)+j;
                }
            }
        }
        if(board[1,1]==0){
            board[1,1]=2;
            return;
        }
        for(int i=0;i<10;i++){
            int temp1=(int)Random.Range(0,4),temp2,temp;
            while((temp2=(int)Random.Range(0,4))==temp1);
            temp=prefer[temp1];
            prefer[temp1]=prefer[temp2];
            prefer[temp2]=temp;
        }
        for(int i=0;i<4;i++){
            if(board[prefer[i]>>2,prefer[i]&3]==0){
                board[prefer[i]>>2,prefer[i]&3]=2;
                return;
            }
        }
        int rd=(int)Random.Range(0,cnt);
        board[chose[rd]>>2,chose[rd]&3]=2;
        return;
    }

    void PVA(){
        for(int i=0;i<3;i++){
            for(int j=0;j<3;j++){
                switch(board[i,j]){
                    case 0:
                        if(turn==0){
                            if(GUI.Button(new Rect(Screen.width/2-120+i*100,Screen.height/2-140+j*100,100,100)," ")&&check()==0){
                                board[i,j]=turn+1;
                                turn=1-turn;
                            }    
                        }
                        else{
                            GUI.Button(new Rect(Screen.width/2-120+i*100,Screen.height/2-140+j*100,100,100)," ");
                            AI();
                            if(check()==0||check()==2) turn=1-turn;
                        }
                        break;
                    case 1:
                        GUI.Button(new Rect(Screen.width/2-120+i*100,Screen.height/2-140+j*100,100,100),"O");
                        break;
                    case 2:
                        GUI.Button(new Rect(Screen.width/2-120+i*100,Screen.height/2-140+j*100,100,100),"X");
                        break;
                }
            }
        }
    }

    // Start is called before the first frame update
    void Start()
    {
        init();
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    void OnGUI (){
        GUI.Box(new Rect(Screen.width/2-300,Screen.height/2-200,600,400), "TicTacToe");
        int state=check();
        switch(state){
            case 0:
                GUI.Box(new Rect(Screen.width/2-70,Screen.height/2-170,200,25),"进行中, 玩家"+(turn+1)+"执棋");
                break;
            case 1:
            case 2:
                GUI.Box(new Rect(Screen.width/2-70,Screen.height/2-170,200,25),"玩家"+(2-turn)+"获胜");
                break;
            case 3:
                GUI.Box(new Rect(Screen.width/2-70,Screen.height/2-170,200,25),"平局");
                break;
        }
        if(GUI.Button(new Rect(Screen.width/2-280,Screen.height/2,100,25),"重置")) init();
        if(GUI.Button(new Rect(Screen.width/2-280,Screen.height/2-120,100,25),"玩家"+(initturn+1)+"先手")){
            initturn=1-initturn;
            init();
        }
        string temp;
        if(mode==0){
            temp="玩家";
        }
        else{
            temp="AI";
        }
        if(GUI.Button(new Rect(Screen.width/2-280,Screen.height/2-90,100,25),"玩家2: "+temp)){
            mode=1-mode;
            init();
        }
        if(mode==0){
            PVP();
        }
        else{
            PVA();
        }
    }
}

3. Thinking questions [optional]

The Game object of the Microsoft XNA engine shields the details of the game loop and uses a set of virtual methods to allow the successor to complete them. We call this design the "template method pattern".

Why is it the "template method" mode instead of the "strategy mode"?

The template method mode has good maintainability and vertical scalability, but the coupling is high, and the subclass cannot affect the parent class's public module code; but the strategy mode has good horizontal scalability and high flexibility, but the client needs to know all the strategies , If too many strategies will lead to increased complexity.

In general, for the Game object of the Microsoft XNA engine, the template method with good maintainability is more practical, which can reduce the complexity and prevent the client from knowing all the strategies.

The game objects are organized into a tree structure, and each node is a game object (or number).

Try to explain the composite pattern (Composite Pattern / a design pattern).

The composite pattern, also called the partial overall pattern, is used to treat a group of similar objects as a single object. It combines objects based on a tree structure to represent part and overall levels. This type of design pattern belongs to the structural pattern. It creates a class that contains its own object group, and at the same time this class provides a way to modify the same object group.

Use the BroadcastMessage() method to send a message to the child object. Can you write the pseudo code of BroadcastMessage()?

void BroadcastMessage(string fun){
	foreach(child of this){
		if(child.hasFunction(fun)){
			child.stringToFunction(fun)();
		}
	}
}

A game object uses many parts to describe the characteristics of different aspects. We design the Tank game object not to inherit from the GameObject object, but to add a set of behavior components to the GameObject.

What design pattern is this?

Decorative pattern

Why not inherit and design special game objects?

Excessive inheritance can easily lead to structural confusion between classes.

"Decorator Pattern allows adding new functionality to an existing object without changing its structure." Dynamically add some additional responsibilities to an object. In terms of increasing functionality, the decorator pattern is more flexible than generating subclasses.

Guess you like

Origin blog.csdn.net/qq_43278234/article/details/108681913