3D游戏编程——游戏对象与图形基础

基本操作演练

下载 Fantasy Skybox FREE, 构建自己的游戏场景

首先我们下载天空盒:
在这里插入图片描述
然后:Assets 上下文菜单 -> create -> Material 起名 mysky
在这里插入图片描述
在 Inspector 视图中选择 Shader -> Skybox -> 6Sided
在这里插入图片描述
在资源贴图中选择合适的图片,拖放到对应位置
在这里插入图片描述
在 Camera 对象中添加部件 Rendering -> Skybox
在这里插入图片描述
将天空盒拖放入 Skybox
在这里插入图片描述
运行游戏,看到下面的游戏画面:
在这里插入图片描述
天空盒应用成功。

除了天空盒,我们还可以添加Terrain,来改变地形,添加树木、花草等。如下图:
在这里插入图片描述

写一个简单的总结,总结游戏对象的使用

游戏对象主要包括以下几类:

  • 常见游戏对象
    如正方体,球体,平面等等,还有空对象,不显示却是最常用的对象之一。
  • Camera 摄像机
    它是观察游戏世界的窗口,Projection属性包括正交视图与透视视图。Viewport Rect:属性是控制摄像机呈现结果对应到屏幕上的位置以及大小。屏幕坐标系:左下角是(0, 0),右上角是(1, 1)。Depth属性是当多个摄像机同时存在时,这个参数决定了呈现的先后顺序,值越大越靠后呈现。
  • 光源Light
    灯光类型(type)包括平行光(类似太阳光),聚光灯(spot),点光源(point),区域光(area,仅烘培用)。
  • 地形构造工具Terrain
    可以构建自己的游戏场景

游戏对象中包含许多的组件,而正是这些组件使得游戏对象可以变得丰富多彩,例如:Transform决定游戏对象的位置、角度、对象。可通过C#动态改变Transform属性以实现物体的移动。通过改变物体的material可以赋予它们不同的外观。在制作游戏对象时,我们可以将它们做成预制,从而减少我们的工作量。

编程实践

牧师与魔鬼 动作分离版

  • 【2019新要求】:设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束

在我上一次实现的代码中,动作是没有完成分离的,也就是说,代表人物和船的控制器的类里面,既包含了游戏对象的状态信息,也包含了它们动作的实现,包括人物的上船、下船以及船的移动。

我们先来看一下原来的控制器的实现(以人为例):

public class peopleController
{

    GameObject people;
    private string status;
    public string size;
    private string defaultSize;
    firstScenceSolveClick solveClick;
    int number;

    public peopleController(string name, int number, Vector3 pos, string status, string size)
    {
        people = Object.Instantiate(Resources.Load("Prefabs/" + name, typeof(GameObject))
            , pos, Quaternion.identity, null) as GameObject;
        people.name = name + number.ToString();
        solveClick = people.AddComponent(typeof(firstScenceSolveClick)) as firstScenceSolveClick;
        solveClick.setName(people.name);
        this.number = number;
        this.status = status;
        defaultSize = size;
        this.size = size;
    }



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

    public string getStatus()
    {
        return status;
    }


    public void getOnBoat(boatController boatCtrl)
    {
        status = "boat";
        people.transform.parent = boatCtrl.getBoat().transform;
        people.transform.localPosition = boatCtrl.getBoatPos(getName());

    }

    public void getOffBoat(environmentController envCtrl)
    {
        status = "shore";
        people.transform.parent = null;
        people.transform.position = envCtrl.getPosVec(size, number);
    }

    public void reset(environmentController envCtrl)
    {
        status = "shore";
        size = defaultSize;
        people.transform.parent = null;
        people.transform.position = envCtrl.getPosVec(size, number);
    }

}

这里面是实现了上船和下船的动作。

我们下面要做的,是把这些动作的实现放在一个新的类中。于是我新创建了一个Action类,在里面实现这些动作,下面是Action类中上船、下船的实现:

    public void getOnBoat(boatController boatCtrl,peopleController peopleCtrl)
    {
        peopleCtrl.status = "boat";
        peopleCtrl.people.transform.parent = boatCtrl.getBoat().transform;
        peopleCtrl.people.transform.localPosition = boatCtrl.getBoatPos(peopleCtrl.getName());

    }

    public void getOffBoat(environmentController envCtrl, peopleController peopleCtrl)
    {
        peopleCtrl.status = "shore";
        peopleCtrl.people.transform.parent = null;
        peopleCtrl.people.transform.position = envCtrl.getPosVec(peopleCtrl.size, peopleCtrl.number);
    }

在这里完成了上船和下船,而新的人的控制器代码中的getOnBoat和getOffBoat函数删去(下面注释掉了这两个函数):

public class peopleController
{

    public GameObject people;
    public string status;
    public string size;
    private string defaultSize;
    firstScenceSolveClick solveClick;
    public int number;

    public peopleController(string name, int number, Vector3 pos, string status, string size)
    {
        people = Object.Instantiate(Resources.Load("Prefabs/" + name, typeof(GameObject))
            , pos, Quaternion.identity, null) as GameObject;
        people.name = name + number.ToString();
        solveClick = people.AddComponent(typeof(firstScenceSolveClick)) as firstScenceSolveClick;
        solveClick.setName(people.name);
        this.number = number;
        this.status = status;
        defaultSize = size;
        this.size = size;
    }



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

    public string getStatus()
    {
        return status;
    }


    /*public void getOnBoat(boatController boatCtrl)
    {
        status = "boat";
        people.transform.parent = boatCtrl.getBoat().transform;
        people.transform.localPosition = boatCtrl.getBoatPos(getName());

    }*/

    /*public void getOffBoat(environmentController envCtrl)
    {
        status = "shore";
        people.transform.parent = null;
        people.transform.position = envCtrl.getPosVec(size, number);
    }*/

    public void reset(environmentController envCtrl)
    {
        status = "shore";
        size = defaultSize;
        people.transform.parent = null;
        people.transform.position = envCtrl.getPosVec(size, number);
    }

}

相应的,在场记的代码中,原来的getOnBoat和getOffBoat函数(在场记中是一个函数getBoatOrGetShore)的实现如下:

    public void getBoatOrGetShore(string name)
    {
        if (myBoat.getRunningState() != "waiting")
        {
            return;
        }
        int numberOfPeople = name[name.Length - 1] - '0';
        if (peopleCtrl[numberOfPeople].getStatus() == "shore")
        {
            if (myBoat.ifHaveSeat() && myBoat.size == peopleCtrl[numberOfPeople].size)
            {
                peopleCtrl[numberOfPeople].getOnBoat(myBoat);
            }
        }
        else
        {
            if (myBoat.size == peopleCtrl[numberOfPeople].size)
            {
                peopleCtrl[numberOfPeople].getOffBoat(environment);
                myBoat.outBoat(peopleCtrl[numberOfPeople].getName());
            }
        }

    }

即调用人控制器的自身的getOnBoat和getOffBoat函数。
我们改了之后的getBoatOrGetShore函数实现如下:

    public void getBoatOrGetShore(string name)
    {
        if (myBoat.getRunningState() != "waiting")
        {
            return;
        }
        int numberOfPeople = name[name.Length - 1] - '0';
        if (peopleCtrl[numberOfPeople].getStatus() == "shore")
        {
            if (myBoat.ifHaveSeat() && myBoat.size == peopleCtrl[numberOfPeople].size)
            {
                myAction.getOnBoat(myBoat, peopleCtrl[numberOfPeople]);
            }
        }
        else
        {
            if (myBoat.size == peopleCtrl[numberOfPeople].size)
            {
                myAction.getOffBoat(environment, peopleCtrl[numberOfPeople]);
                myBoat.outBoat(peopleCtrl[numberOfPeople].getName());
            }
        }

    }

可以看到,现在是场记让myAction(实例化的Action变量)来完成人的上船、下船移动,即实现了动作的分离。
船的移动的改变和人的上船下船的改变类似,在这里不用多说。

下面是新的要求:设计一个裁判类,当游戏达到结束条件时,通知场景控制器游戏结束。
我们新建一个类Judge,代码如下:

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

public class Judge : MonoBehaviour
{
    // Start is called before the first frame update

    public string status;

    void Awake()
    {
        status = "playing";
    }

    void Start()
    {
        status = "playing";
    }

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

    public Judge()
    {
        status = "playing";
    }

    public string getStatus()
    {
        return status;
    }

    public void setStatus(string s)
    {
        status = s;
    }

}

里面就只是简单实现了getStatus和setStatus函数,下面则要修改场景控制器里面跟游戏结束判断相关的部分,场景控制器中的getStatus函数修改如下:

public string getStatus()
    {
        return myJudge.getStatus();
    }

其中myJudge是Judge的实例化对象。
而在游戏状态发生变化时,也应该改变myJudge,如下:

    void Update()
    {
        int leftDevil = 0, leftPriest = 0, rightDevil = 0, rightPriest = 0, leftShorePeople = 0;
        for (int loop = 0; loop < numOfPirestOrDevil * 2; loop++)
        {
            if (peopleCtrl[loop].getStatus() == "shore" && peopleCtrl[loop].size == "left")
            {
                leftShorePeople++;
            }
            if (peopleCtrl[loop].getName()[0] == 'd' && peopleCtrl[loop].size == "left")
            {
                leftDevil++;
            }
            else if (peopleCtrl[loop].getName()[0] == 'd' && peopleCtrl[loop].size == "right")
            {
                rightDevil++;
            }
            else if (peopleCtrl[loop].getName()[0] == 'p' && peopleCtrl[loop].size == "left")
            {
                leftPriest++;
            }
            else
            {
                rightPriest++;
            }
        }
        if ((leftDevil > leftPriest && leftPriest != 0) || (rightPriest != 0 && rightDevil > rightPriest))
        {
            myJudge.setStatus("lost");
        }
        else if (leftShorePeople == 6)
        {
            myJudge.setStatus("win");
        }


    }

这样游戏达到结束条件时,是裁判类通知场景控制器游戏结束。到这里我们的工作就基本上完成了。
游戏效果图:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值