3D game第三次作业(《牧师与魔鬼》)

本文详细介绍了3D游戏开发中的关键技术,包括游戏对象运动的实现方法、Unity3D环境下使用C#编程完成牧师与魔鬼游戏的具体步骤。通过实例演示如何运用不同技术手段实现抛物线运动,并分享了一个完整的游戏项目案例。
摘要由CSDN通过智能技术生成

3D game第三次作业

1.游戏对象运动的本质是什么?

对象位置属性,方向属性等的变化。

2.请用三种方法以上方法,实现物体的抛物线运动。(如,修改Transform属性,使用向量Vector3的方法…)

  1. 用导数的方法,不断用当前position加上一个位移向量:

例如:(y=-x^2+4x)

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

public class NewBehaviourScript : MonoBehaviour
{
    public float speed;
 
    // Start is called before the first frame update
    void Start()
    {

    }

    // Update is called once per frame
    void Update()
    {
        float dx=Time.deltaTime*speed;
        Vector3 dir=new Vector3(dx,(-2*this.transform.position.x+4)*dx,0);
        this.transform.position+=dir;

    }
}

结果:
在这里插入图片描述

  1. 用transl函数:
    void Update()
    {
        float dx=Time.deltaTime*speed;
        Vector3 dir=new Vector3(dx,(-2*this.transform.position.x+4)*dx,0);
        this.transform.Translate(dir);
    }

结果同上。

  1. 用lerp
 void Update()
    {
        float dx=Time.deltaTime*speed;
        Vector3 dir=new Vector3(dx,(-2*this.transform.position.x+4)*dx,0);
        this.transform.position=Vector3.Lerp(this.transform.position,this.transform.position+dir,1);

    }

结果同上。

3.编程实践《牧师与魔鬼》


结果展示

在这里插入图片描述


先附上项目链接(文末也有)

你可以直接下载我的项目,解压后用unity打开(2020版本以上)

点击这里下载项目压缩包(29 MB): 项目下载


如果不想下载,可以在这里查看代码

代码传送门:代码传送门


首先必须告诉第一次接触MVC结构的小朋友

unity 3d的所有c#代码都是放在一起编译的。
就算你放在仓库里不用,或者放在不用的文件夹里,他们都是在一起编译的。
可以相互调用彼此的类,或者static变量。
因此关于导演类Direct,场记类FirstController放在哪里,就不用多说了吧。
导演类就放在Asset里睡觉,场记拖到照相机上

游戏中提及的物体

左河岸,右河岸,水,牧师,魔鬼,船。

玩家动作表

动作条件结果
点击人物人物与船在同一海岸,船静止人下船或者人上船
点击船船静止,且船上必须有人船和船上的人移到另一岸
点击重玩按钮船不动时即可触发所有物体和数据恢复初始状态

具体代码

(具体原理见课件

导演类Direct和一些interface

Direct.cs
导演类负责指明当前正在工作的是哪个场景,这个场景具体干什么导演并不关心,导演只负责管理一些重要的参数,如gamestatus表示游戏是否结束。
interface提供了导演与场景沟通的接口,具体怎么做,取决于各个场景的场记

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

public class Direct : System.Object
{
    public static int gameStatus;
    public static Direct instance;
    public ISceneController currentScene{get;set;}
    public static Direct getInstance(){
        if (instance== null){
            instance=new Direct();
        }
        return instance;
    }

}

public interface ISceneController{
    void loadResources();
}

public interface UserAction{
    void pndOnClick(GameObject gameObject);
    void moveBoot();
    void restart();
}
各个物体类

用于生成各个物体,并记录物体的关键参数。
比如boot类的bl,br分别指向船上坐在左边或右边的人。
leftcoast中的ptr[]指向左海岸的6个位置,如果没人就是null。
moveStatus出现在牧师魔鬼类(pnd)和船类(boot)上,记录运动状态。

objectClass.cs

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

public class leftCoast{
    public GameObject obj;
    public Vector3 position;
    public GameObject[] ptr;

    public leftCoast(){
        ptr=new GameObject[7];
        for(int i=1;i<=6;++i)
            ptr[i]=null;
        position=new Vector3(-15,0,0);
        obj=Object.Instantiate(Resources.Load<GameObject>("profeb/coast"),position,Quaternion.identity);
        obj.name="leftCoast";
    }

}

public class rightCoast{
    public GameObject obj;
    public Vector3 position;
    public GameObject[] ptr;
    

    public rightCoast(){
        ptr=new GameObject[7];
        for(int i=1;i<=6;++i)
            ptr[i]=null;
        position=new Vector3(15,0,0);
        obj=Object.Instantiate(Resources.Load<GameObject>("profeb/coast"),position,Quaternion.identity);
        obj.name="rightCoast";
    }
}

public class water{
    public GameObject obj;
    public Vector3 position;

    public water(){
        position=new Vector3(0,-0.5f,0);
        obj=Object.Instantiate(Resources.Load<GameObject>("profeb/water"),position,Quaternion.identity);
        obj.name="water";
        obj.AddComponent(typeof(OnClick));
    }
}

public class  pnd{
    public GameObject obj;
    public int pos;

    public Vector3 target;

    public int moveStatus;

    public pnd(int kind,Vector3 position){
        pos=1;
        moveStatus=0;

        if (kind==1){
                obj=Object.Instantiate(Resources.Load<GameObject>("profeb/priest"),position,Quaternion.identity);
                obj.name="priest";
                obj.AddComponent(typeof(OnClick));

        }
        
        if (kind==2){
            obj=Object.Instantiate(Resources.Load<GameObject>("profeb/devil"),position,Quaternion.identity);
            obj.name="devil";
            obj.AddComponent(typeof(OnClick));

        }
    }
}


public class boot{
    public GameObject obj;
    public  Vector3 bootl,bootr;
    public GameObject l,r;
    public static Vector3 lv,rv;

    public int side;

    public int moveStatus;

    public boot(){
        bootl=new Vector3(-3.5f,2,0);
        bootr=new Vector3(3.5f,2,0);

        obj=Object.Instantiate<GameObject>(Resources.Load<GameObject>("profeb/boot"),bootl,Quaternion.identity);
        obj.name="boot";
        side=1;

        lv=new Vector3(-0.75f,0.9f,0);
        rv=new Vector3(0.75f,0.9f,0);
        l=null;r=null;

        moveStatus=0;

        obj.AddComponent(typeof(OnClick));       
      

       
    }

    
}
场记类

最核心的类。
用于调度各个物体的生成。
继承了UserAction接口,用于处理游戏中发生的一切事件。
并与导演类联系,以确定游戏是否结束等重要事件。

FirstController.cs

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

public class FirstController : MonoBehaviour,ISceneController,UserAction
{
    public  Vector3[] leftpos;
    public  Vector3[] rightpos;
    public  int[] leftseat;
    public  int[] rightseat;

    public  leftCoast lc;
    public  rightCoast rc;
    public water wat;
    public  pnd[] p;
    public  boot b;


    
    // Start is called before the first frame update
    void Awake(){
        Direct direct=Direct.getInstance();
        direct.currentScene=this;
    }

    void Start()
    {
        leftpos=new Vector3[7]{new Vector3(0,0,0),new Vector3(-6,3,0),new Vector3(-8,3,0),new Vector3(-10,3,0),
        new Vector3(-12,3,0),new Vector3(-14,3,0),new Vector3(-16,3,0)};

        rightpos=new Vector3[7]{new Vector3(0,0,0),new Vector3(6,3,0),new Vector3(8,3,0),new Vector3(10,3,0),
        new Vector3(12,3,0),new Vector3(14,3,0),new Vector3(16,3,0)};

        leftseat=new int[7];
        rightseat=new int[7];

        for(int i=1;i<=6;++i){
            leftseat[i]=0;
            rightseat[i]=0;
        }
        leftseat[1]=1;leftseat[2]=1;leftseat[3]=1;
        leftseat[4]=2;leftseat[5]=2;leftseat[6]=2;


        Direct.gameStatus=0;

        loadResources();
    }

    // Update is called once per frame

    void Update()
    {

        if (b.moveStatus!=0){
            for(int i=1;i<=6;++i){
                if (p[i].moveStatus==1||p[i].moveStatus==2){
                    p[i].obj.transform.position=Vector3.MoveTowards(p[i].obj.transform.position,p[i].target,0.06f);
                }

                if (p[i].obj.transform.position==p[i].target){
                    p[i].moveStatus=0;
                }
            }

            if (b.moveStatus==1){

                b.obj.transform.position=Vector3.MoveTowards(b.obj.transform.position,b.bootr,0.06f);
                if (b.obj.transform.position==b.bootr){
                    b.moveStatus=0;
                    b.side=2;
                    Direct.gameStatus=check();
                }

            }else if (b.moveStatus==2){
                
                b.obj.transform.position=Vector3.MoveTowards(b.obj.transform.position,b.bootl,0.06f);
                if (b.obj.transform.position==b.bootl){
                    b.moveStatus=0;
                    b.side=1;
                    Direct.gameStatus=check();
                }

            }

        }


    }

    public void loadResources(){
        lc=new leftCoast();
        rc=new rightCoast();
        wat=new water();
        
        p=new pnd[7];
        for(int i=1;i<=6;++i){
            p[i]=new pnd(leftseat[i],leftpos[i]);
            lc.ptr[i]=p[i].obj;
        }
        


        b=new boot();
        gameObject.AddComponent(typeof(MyGUI));
    }

    public void moveBoot(){
        if (b.moveStatus!=0) return;
        if (b.l==null && b.r==null) return;

        for(int i=1;i<=6;++i){
            if (p[i].obj==b.l || p[i].obj==b.r){
                p[i].moveStatus=b.side;
                if (b.side==1) p[i].target=p[i].obj.transform.position+new Vector3(7,0,0);
                else p[i].target=p[i].obj.transform.position+new Vector3(-7,0,0);
            }
        }
        b.moveStatus=b.side;
    }

    public void pndOnClick(GameObject gameObject){
            if (b.moveStatus!=0) return;

            if (b.l!=gameObject && b.r!=gameObject){// 不在船上
                int objside=0;
                int index=0;
                for(int i=1;i<=6;++i){
                    if (lc.ptr[i]==gameObject){
                        objside=1;
                        index=i;
                        break;
                    }

                    if (rc.ptr[i]==gameObject){
                        objside=2;
                        index=i;
                        break;
                    }
                }

                if (objside!=b.side){ //人与船不在同一海岸,则不上
                    return;
                }

                if (b.l==null){//上船,船满则不上
                    b.l=gameObject;

                    if (objside==1)
                        lc.ptr[index]=null;
                    else
                        rc.ptr[index]=null;

                    gameObject.transform.position=b.obj.transform.position+boot.lv;
                    
                }else{
                    if (b.r==null){
                        b.r=gameObject;

                        if (objside==1)
                            lc.ptr[index]=null;
                         else
                            rc.ptr[index]=null;

                        gameObject.transform.position=b.obj.transform.position+boot.rv;
                    }
                }

            }else{  //在船上
                if (b.side==1){ // 在左海岸
                    for(int i=1;i<=6;++i){
                        if (lc.ptr[i]==null){
                            gameObject.transform.position=leftpos[i];
                            lc.ptr[i]=gameObject;

                            if (b.l==gameObject)
                                b.l=null;
                            else 
                                b.r=null;

                            break;
                        }
                    }
                }else{// 在右海岸
                    for(int i=1;i<=6;++i){
                        if (rc.ptr[i]==null){
                            gameObject.transform.position=rightpos[i];
                            rc.ptr[i]=gameObject;

                            if (b.l==gameObject)
                                b.l=null;
                            else 
                                b.r=null;

                            break;
                        }
                    }
                }
            }

        Direct.gameStatus=check();
    }


    int check(){
        int p,d;
        p=0;d=0;
        for(int i=1;i<=6;++i){
            if (lc.ptr[i]==null) continue;
            if (lc.ptr[i].name=="priest")
                p++;
            if (lc.ptr[i].name=="devil")
                d++;
        }
        if (b.side==1){
            if (b.l!=null){
                if (b.l.name=="priest") p++;
                if (b.l.name=="devil") d++;
            }
            if (b.r!=null){
                if (b.r.name=="priest") p++;
                if (b.r.name=="devil") d++;
            }
        }
        if (d>p && p!=0){
            return -1;
        }

        p=0;d=0;
        for(int i=1;i<=6;++i){
            if (rc.ptr[i]==null) continue;
            if (rc.ptr[i].name=="priest")
                p++;
            if (rc.ptr[i].name=="devil")
                d++;
        }
        if (b.side==2){
            if (b.l!=null){
                if (b.l.name=="priest") p++;
                if (b.l.name=="devil") d++;
            }
            if (b.r!=null){
                if (b.r.name=="priest") p++;
                if (b.r.name=="devil") d++;
            }
        }
        if (d>p && p!=0){
            return -1;
        }

        int number=0;
        for(int i=1;i<=6;++i){
            if (rc.ptr[i]!=null)
                number++;
        }
        if (number==6)
            return 1;
        return 0;

    }

    public void restart(){
        for(int i=1;i<=6;++i){
            p[i].obj.transform.position=leftpos[i];
            lc.ptr[i]=p[i].obj;
            rc.ptr[i]=null;
            p[i].moveStatus=0;
        }
        b.l=null;
        b.r=null;
        b.obj.transform.position=b.bootl;
        b.side=1;
        b.moveStatus=0;
        Direct.gameStatus=0;

    }
}
GUI类

处理GUI信息,比如胜败,重玩按钮,标题等
还有右上角那么性感的X
通过向导演类询问游戏状态(胜利失败什么的)来决定屏幕上显示什么。
也要UserAction处理玩家动作(只有重玩按钮需要)

MyGUI.cs

public class MyGUI : MonoBehaviour
{
    UserAction action;
    public int w,h;
    public Texture2D title,restart,over,win,esc;
    // Start is called before the first frame update
    void Start()
    {
        action=Direct.getInstance().currentScene as UserAction;
        w=Screen.width;
        h=Screen.height;

        title=Resources.Load<Texture2D>("img/title");
        restart=Resources.Load<Texture2D>("img/restart");
        over=Resources.Load<Texture2D>("img/over");
        win=Resources.Load<Texture2D>("img/win");
        esc=Resources.Load<Texture2D>("img/esc");
    }

    // Update is called once per frame
    void Update()
    {
        w=Screen.width;
        h=Screen.height;

        
    }

    void OnGUI(){
        

        GUI.Label(new Rect(w*0.26f,0,w*0.48f,w*0.12f),title);
        if ( GUI.Button(new Rect(w-100,0,100,100),esc)){
            Application.Quit();
        }
        if ( GUI.Button(new Rect(w*0.45f,h-w*0.1f,w*0.1f,w*0.05f),restart) ){
            action.restart();
        }
        
        if (Direct.gameStatus==-1){
            GUI.Label(new Rect(0.5f*w-100,0.5f*h-100,200,200),over);
        }
        if (Direct.gameStatus==1){
            GUI.Label(new Rect(0.5f*w-100,0.5f*h-100,200,200),win);
        }
    }
}
点击事件类

处理点击事件,点了人或者船。
并通过UserAction告诉场记FirstController应该做什么事。调用FirstController中的函数。
OnMouseDown()表示点了3d物体。

OnClick.cs

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

public class OnClick : MonoBehaviour
{

    UserAction action;

    void Start(){
        action=Direct.getInstance().currentScene as UserAction;
    }

    void OnMouseDown(){
        if (Direct.gameStatus==-1 || Direct.gameStatus==1) return;

       if (gameObject.name=="priest" || gameObject.name=="devil"){//点了牧师或魔鬼
           action.pndOnClick(gameObject);
        
       }

       if (gameObject.name=="boot"){
           action.moveBoot();
       }
    }




}

至此完成了代码的简单解析。

以下是项目链接

你可以直接下载我的项目,解压后用unity打开(2020版本以上)

点击这里下载项目压缩包(29 MB): 项目下载


如果不想下载,可以在这里查看代码

代码传送门:代码传送门

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值