3D game (3)-space and movement

1. Short answer and use program verification [recommended]

What is the nature of the movement of the game object?

The movement process of the game object is essentially the three properties of the game object's spatial position (Position), rotation angle (Rotation), and size (Scale) making certain specific changes over time.

Please use the above three methods to realize the parabolic motion of the object. (For example, to modify the Transform property, use the Vector3 method...)

method 1:

For compound movement, first write a script that moves to the right, then write a script that moves downward, and finally mount the two scripts to the same object at the same time.

Move to the right:

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

public class MoveRight : MonoBehaviour
{

    public int speed = 5;
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        this.transform.position += speed * Vector3.right * Time.deltaTime;
    }
}

Downward movement:

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

public class MoveDown : MonoBehaviour
{

    public float speed = 2;
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        this.transform.position += speed * Vector3.down * Time.deltaTime;
        speed += 0.1f;
    }
}

Method 2:

Use Vector3 directly to change the position and merge the two scripts into one.

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

public class Move : MonoBehaviour
{

    public int speedx = 5;
    public float speedy = 2;
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        transform.position += new Vector3(speedx * Time.deltaTime, -1 * speedy * Time.deltaTime, 0);
        speedy += 0.1f;
    }
}

Method 3:

Use transform.Translate to pass the change vector obtained in method 2 into the Translate function.

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

public class Move : MonoBehaviour
{

    public int speedx = 5;
    public float speedy = 2;
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        transform.Translate(new Vector3(speedx * Time.deltaTime, -1 * speedy * Time.deltaTime, 0));
        speedy += 0.1f;
    }
}

Write a program to realize a complete solar system. The rotation speed of other planets around the sun must be different and not on a normal plane.

First go to Hongdong.com to download the texture material of the solar system. Place the planets in the solar system.

Insert picture description here

Refer to exercise 03-09, first complete a solar system on the same normal plane.

Insert picture description here

If the planets are not on the same normal plane, a random number can be introduced and a normal vector can be randomly generated. This avoids designing a script for each planet. However, since the earth also has a satellite (the moon), a script needs to be specially designed. Therefore, in general, only two scripts are required.

Script for planets outside the earth:

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

public class Round : MonoBehaviour
{
    public Transform center;
    public int y,z,speed;
    // Start is called before the first frame update
    void Start()
    {
        y = Random.Range(-10, 10);
        z = Random.Range(-10, 10);
        speed = Random.Range(10, 100);
    }

    // Update is called once per frame
    void Update()
    {
        transform.RotateAround(center.transform.position, new Vector3(0, y, z), speed * Time.deltaTime);
    }
}

Earth's orbit script:

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

public class RoundOfEarth : MonoBehaviour
{
    public Transform sun;
    public Transform moon;
    public int y,z,speed;
    // Start is called before the first frame update
    void Start()
    {
        y = Random.Range(-10, 10);
        z = Random.Range(-10, 10);
        speed = Random.Range(10, 100);
    }

    // Update is called once per frame
    void Update()
    {
        this.transform.RotateAround(sun.position, new Vector3(0, y, z), speed * Time.deltaTime);
        moon.transform.RotateAround(this.transform.position, new Vector3(0, y, z), 20 * speed * Time.deltaTime);
    }
}

Finally, the respective scripts are mounted on each planet.

Insert picture description here

2. Programming practice

Read the following game script

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!

play the game

The game is very simple. First send a devil to the other side (the priest and the devil pass, the priest comes back, or two demons pass and one devil comes back); then send the second devil to the past (this time requires two demons to pass); then two priests cross the river , The priest took a devil back; the two priests crossed the other side, and finally let the devil transport the remaining two devils over.

List the things mentioned in the game (Objects)

Priest, demon, boat, river, banks

List the player action table (rule table) in a table. Note that the fewer actions the better

Current state Player action result
There are vacancies on board Click on the demon or priest on the shore near the side of the ship Corresponding demon or priest aboard
Priest or demon on the boat Click on the demon or priest on the ship Corresponding demon or priest on the shore near the ship
The number of demons on either side is greater than the number of priests game over
The demon and priest all go to the other side of the river Game victory

Please make the objects in the game prefab

Download scene

Insert picture description here

Download character model

Insert picture description here

Make game objects into prefabs:

Insert picture description here

Start programming

First, download the template of the MVC structure program . A simple solar system is implemented in the code.

Insert picture description here

Both SSDirector and ISceneController can directly use templates, but IUserAction also needs to add new characters and ship mobile interfaces, and an additional game status check interface is also added, namely:

Insert picture description here

Then the OnGUI function in UserGUI was slightly modified, and the information about the success or failure of the game was added.

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

public class UserGUI : MonoBehaviour {

	private IUserAction action;
	public string gameMessage;

	void Start () {
		action = SSDirector.getInstance ().currentSceneController as IUserAction;
	}

	void OnGUI() {  
		float width = Screen.width / 6;  
		float height = Screen.height / 12;

		action.Check();
		GUIStyle style = new GUIStyle();
        style.normal.textColor = Color.red;
        style.fontSize = 30;
		GUI.Label(new Rect(320,100,50,200),gameMessage,style);

		if (GUI.Button(new Rect(0, 0, width, height), "Restart")) {  
			action.Restart();  
		} 

		string paused_title = SSDirector.getInstance ().Paused ? "Resume" : "Pause!"; 
		if (GUI.Button(new Rect(width, 0, width, height), paused_title)) { 
			SSDirector.getInstance ().Paused = !SSDirector.getInstance ().Paused;
		} 
	}
}

Then it's the most important part, that is , the preparation of FirstController . The first thing you need are various controllers and various state variables of the current game:

	private LandModelController landRoleController;
	private BoatController boatRoleController;
	private RoleModelController[] roleModelControllers;
	private MoveController moveController;
	private bool isRuning;
	private int leftPriestNum;
	private int leftDevilNum;
	private int rightPriestNum;
	private int rightDevilNum;

The original LoadResources function needs to be modified. Here, only the corresponding Controller needs to be created, and the specific prefabricated instantiation is handed over to the corresponding Controller.

	public void LoadResources () {
		landRoleController=new LandModelController();
		landRoleController.CreateLand();
		roleModelControllers=new RoleModelController[6];
		for(int i=0;i<6;i++){
			roleModelControllers[i]=new RoleModelController();
			roleModelControllers[i].CreateRole(i<3? true:false,i);
			roleModelControllers[i].GetRoleModel().role.transform.localPosition=landRoleController.AddRole(roleModelControllers[i].GetRoleModel());
		}
		boatRoleController=new BoatController();
		boatRoleController.CreateBoat();
		moveController=new MoveController();
		leftPriestNum=leftDevilNum=3;
		rightPriestNum=rightDevilNum=0;
		isRuning=true;
	}

At the same time, it is also necessary to implement the four interfaces defined in IUserAction. The implementation of these functions is based on the rules of the game, and the corresponding Controller only needs to be called.

	#region IUserAction implementation
	public void MoveBoat(){
		if(!isRuning||moveController.GetIsMoving()) return;
		if(boatRoleController.GetBoatModel().isRight){
			moveController.SetMove(new Vector3(3,-0.3f,-30),boatRoleController.GetBoatModel().boat);
			leftPriestNum+=boatRoleController.GetBoatModel().priestNum;
			leftDevilNum+=boatRoleController.GetBoatModel().devilNum;
			rightPriestNum-=boatRoleController.GetBoatModel().priestNum;
			rightDevilNum-=boatRoleController.GetBoatModel().devilNum;
		}
		else{
			moveController.SetMove(new Vector3(7.5f,-0.3f,-30),boatRoleController.GetBoatModel().boat);
			leftPriestNum-=boatRoleController.GetBoatModel().priestNum;
			leftDevilNum-=boatRoleController.GetBoatModel().devilNum;
			rightPriestNum+=boatRoleController.GetBoatModel().priestNum;
			rightDevilNum+=boatRoleController.GetBoatModel().devilNum;
		}
		boatRoleController.GetBoatModel().isRight=!boatRoleController.GetBoatModel().isRight;
	}
	public void MoveRole(RoleModel roleModel){
		if(!isRuning||moveController.GetIsMoving()) return;
		if(roleModel.isInBoat){
			roleModel.isRight=boatRoleController.GetBoatModel().isRight;
			moveController.SetMove(landRoleController.AddRole(roleModel),roleModel.role);
			boatRoleController.RemoveRole(roleModel);
		}
		else if(boatRoleController.GetBoatModel().isRight==roleModel.isRight){
			landRoleController.RemoveRole(roleModel);
			moveController.SetMove(boatRoleController.AddRole(roleModel),roleModel.role);
		}
	}
	public void Restart ()
	{
		landRoleController.CreateLand();
		for(int i=0;i<6;i++){
			roleModelControllers[i].CreateRole(i<3? true:false,i);
			roleModelControllers[i].GetRoleModel().role.transform.localPosition=landRoleController.AddRole(roleModelControllers[i].GetRoleModel());
		}
		boatRoleController.CreateBoat();
		leftPriestNum=leftDevilNum=3;
		rightPriestNum=rightDevilNum=0;
		isRuning=true;
		this.gameObject.GetComponent<UserGUI>().gameMessage="";
	}
	public void Check(){
		if(!isRuning) return;
		this.gameObject.GetComponent<UserGUI>().gameMessage="";
		if(rightPriestNum==3&&rightDevilNum==3){
			this.gameObject.GetComponent<UserGUI>().gameMessage="You Win!!";
			isRuning=false;
		}
		else if((leftPriestNum!=0&&leftPriestNum<leftDevilNum)||(rightPriestNum!=0&&rightPriestNum<rightDevilNum)){
			this.gameObject.GetComponent<UserGUI>().gameMessage="Game Over!!";
			isRuning=false;
		}
	}
	#endregion

After implementing FirstController, in this way, the game's framework is almost ready, and then you only need to implement each Controller and the corresponding click event.

It is the character model and the ship that need to add a click event. The handling of this event is slightly different between the two, so a unified interface ClickAction is written here .

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

public interface ClickAction
{
    void DealClick();
}

Therefore, after a click event occurs, just call this interface function.

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

public class Click : MonoBehaviour
{

    ClickAction clickAction;
    public void setClickAction(ClickAction clickAction){
        this.clickAction=clickAction;
    }
    void OnMouseDown(){
        clickAction.DealClick();
    }

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

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

The realization of Move is completely the content of this lesson (space and movement). On the basis of the realization of translational movement, a MoveController is added to manage the current moving objects, because "the ship is not docked, the priest and the devil No user events can be accepted during boarding and disembarking!"

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

public class Move : MonoBehaviour
{

    public bool isMoving=false;
    public float speed=3;
    public Vector3 des;

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

    // Update is called once per frame
    void Update()
    {
        if(transform.localPosition==des){
            isMoving=false;
            return;
        }
        isMoving=true;
        transform.localPosition=Vector3.MoveTowards(transform.localPosition,des,speed*Time.deltaTime);
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MoveController
{

    private GameObject moveObject;
    public bool GetIsMoving(){
        return moveObject!=null&&moveObject.GetComponent<Move>().isMoving;
    }
    public void SetMove(Vector3 des,GameObject moveObject){
        Move test;
        this.moveObject=moveObject;
        if(!moveObject.TryGetComponent<Move>(out test)) moveObject.AddComponent<Move>();
        this.moveObject.GetComponent<Move>().des=des;
    }

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

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

As for the realization of the ship/character/ground, they are all similar. Taking the boat as an example, in addition to implementing the above-mentioned click event processing interface and creating a boat, it is also necessary to manage the embarkation and disembarkation of the characters. As for the ship model and corresponding The state is handed over to the next layer of BoatModel to complete.

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

public class BoatController : ClickAction
{

    BoatModel boatModel;
    IUserAction userAction;

    public BoatController(){
        userAction=SSDirector.getInstance().currentSceneController as IUserAction;
    }
    public void CreateBoat(){
        if(boatModel!=null) Object.DestroyImmediate(boatModel.boat);
        boatModel=new BoatModel();
        boatModel.boat.GetComponent<Click>().setClickAction(this);
    }
    public BoatModel GetBoatModel(){
        return boatModel;
    }
    public Vector3 AddRole(RoleModel roleModel){
        if(boatModel.roles[0]==null){
            boatModel.roles[0]=roleModel;
            roleModel.isInBoat=true;
            roleModel.role.transform.parent=boatModel.boat.transform;
            if(roleModel.isPriest) boatModel.priestNum++;
            else boatModel.devilNum++;
            return new Vector3(-0.2f,0.2f,0.5f);
        }
        if(boatModel.roles[1]==null){
            boatModel.roles[1]=roleModel;
            roleModel.isInBoat=true;
            roleModel.role.transform.parent=boatModel.boat.transform;
            if(roleModel.isPriest) boatModel.priestNum++;
            else boatModel.devilNum++;
            return new Vector3(-0.2f,0.2f,-0.6f);
        }
        return roleModel.role.transform.localPosition;
    }
    public void RemoveRole(RoleModel roleModel){
        roleModel.role.transform.parent=null;
        if(boatModel.roles[0]==roleModel){
            boatModel.roles[0]=null;
            if(roleModel.isPriest) boatModel.priestNum--;
            else boatModel.devilNum--;
        }
        else if(boatModel.roles[1]==roleModel){
            boatModel.roles[1]=null;
            if(roleModel.isPriest) boatModel.priestNum--;
            else boatModel.devilNum--;
        }
    }
    public void DealClick(){
        if(boatModel.roles[0]!=null||boatModel.roles[1]!=null){
            userAction.MoveBoat();
        }
    }

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

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

And BoatModel only needs to instantiate and initialize the boat. But it should be noted that the game object needs to add BoxCollider to be able to trigger the click event.

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

public class BoatModel
{

    public GameObject boat;
    public RoleModel[] roles;
    public bool isRight;
    public int priestNum,devilNum;

    public BoatModel(){
        priestNum=devilNum=0;
        roles=new RoleModel[2];
        boat=GameObject.Instantiate(Resources.Load("WoodBoat", typeof(GameObject))) as GameObject;
        boat.transform.position=new Vector3(3,-0.3f,-30);
        boat.AddComponent<BoxCollider>();
        boat.AddComponent<Click>();
        boat.GetComponent<BoxCollider>().size=new Vector3(1.5f,0.6f,2.5f);
        isRight=false;
    }

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

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

The implementation of the remaining characters and the ground is similar to that of a boat, see the complete code for details .

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

public class RoleModelController : ClickAction
{

    RoleModel roleModel;
    IUserAction userAction;
    public RoleModelController(){
        userAction=SSDirector.getInstance().currentSceneController as IUserAction;
    }
    public void CreateRole(bool isPriest,int tag){
        if(roleModel!=null) Object.DestroyImmediate(roleModel.role);
        roleModel=new RoleModel(isPriest,tag);
        roleModel.role.GetComponent<Click>().setClickAction(this);
    }
    public RoleModel GetRoleModel(){
        return roleModel;
    }
    public void DealClick(){
        userAction.MoveRole(roleModel);
    }

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

    // Update is called once per frame
    void Update()
    {
        
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RoleModel// : MonoBehaviour
{

    public GameObject role;
    public bool isPriest;
    public int tag;
    public bool isRight;
    public bool isInBoat;
    public Vector3 rightPos;
    public Vector3 leftPos;

    public RoleModel(bool isPriest,int tag){
        this.isPriest=isPriest;
        this.tag=tag;
        isRight=false;
        isInBoat=false;
        rightPos=new Vector3(9,0,-33.3f+tag*1.1f);
        leftPos=new Vector3(1,0,-33.3f+tag*1.1f);
        role=GameObject.Instantiate(Resources.Load(isPriest?"Priests"+tag:"Devils", typeof(GameObject))) as GameObject;
        role.transform.position=leftPos;
        role.AddComponent<Click>();
        role.AddComponent<BoxCollider>();
        role.GetComponent<BoxCollider>().size=new Vector3(0.6f,3,0.6f);
    }

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

    // Update is called once per frame
    void Update()
    {
        
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LandModelController
{

    private LandModel landModel;

    public void CreateLand(){
        if(landModel==null) landModel=new LandModel();
    }
    public LandModel GetLandModel(){
        return landModel;
    }
    public Vector3 AddRole(RoleModel roleModel){
        roleModel.role.transform.parent=this.landModel.land.transform;
        roleModel.isInBoat=false;
        if(roleModel.isRight) return roleModel.rightPos;
        else return roleModel.leftPos;
    }
    public void RemoveRole(RoleModel roleModel){

    }

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

    // Update is called once per frame
    void Update()
    {
        
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class LandModel
{

    public GameObject land;
    public LandModel(){
        land=GameObject.Instantiate(Resources.Load("Environment", typeof(GameObject))) as GameObject;
        land.transform.position=new Vector3(0,0,0);
    }

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

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

Insert picture description here

Finally, thank you brother Unity for implementing the Priests and Deivls game

3. Thinking questions [optional]

Use vectors and transforms to implement and extend the methods provided by Tranform, such as Rotate, RotateAround, etc.

Create an empty object mount script.

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

public class test : MonoBehaviour
{

    public Transform obj;
    void Rotate(Vector3 axis,float angle,Space relativeTo=Space.Self){
        var rot=Quaternion.AngleAxis(angle,axis);
        obj.rotation*=rot;
    }
    void RotateAround(Vector3 point,Vector3 axis,float angle){
        var rot=Quaternion.AngleAxis(angle,axis);
        obj.position=point+(obj.position-point)*rot;
        obj.rotation*=rot;
    }
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        Rotate(Vector3.up,30*Time.deltaTime);
    }
}

According to the online statement, RotateAround seems to be able to achieve this, but the obj.position line will report one error CS0019: Operator '*' cannot be applied to operands of type 'Vector3' and 'Quaternion', but obj.rotation does not have such an error message. Does this mean that obj.rotation is not a Vector3 type? ?

But in general, although RotateAround has encountered some problems, Rotate is still feasible.

Insert picture description here

Guess you like

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