游戏对象与图形基础——牧师与魔鬼(动作分离版)

游戏对象与图形基础——牧师与魔鬼(动作分离版)

一、基本操作演练

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

首先在Asset Store中,下载Fantasy Skybox FREE素材包,之后再unity中导入。
在这里插入图片描述
之后,我们可以看到素材包中包含了天空盒、terrain等材料的模型,我们这里直接使用模板的素材来构建游戏场景首先为camera添加天空盒:
在这里插入图片描述
之后将素材包中的SimpleTerrain地形加入场景,我们再用工具对地形进行一定的修改(挖点洞,更改一下地皮,设置地形平缓,种点花花草草),这样我们就完成了游戏场景的设置:‘

在这里插入图片描述
离近点可以看到我们种得草:
在这里插入图片描述

1.2 总结:

  • 首先我们最常见的游戏对象就是角色、地形等的实例游戏对象,就像我们牧师与魔鬼中的船、牧师、魔鬼等实例游戏对象;对这类游戏对象,我们的常见操作有对象的移动、实例化预设加载游戏对象、挂载脚本、设置对象形状属性、添加删改组件等,这是我们游戏过程中最直接操作和接触的游戏对象;
  • 游戏场景中的光照和摄像头是一种辅助对象,用于玩家在实际游戏过程中,对游戏场景进行观察。
  • 空GameObject常常用于挂载脚本,进行资源加载等操作;
  • Terrain等地图编辑对象,可用于游戏地形等的设定。

二、牧师与魔鬼(动作分离版)

2.1 UML图

程序设计的流程图根据老师课件中给出的UML图架构:
在这里插入图片描述

2.2 程序实现:

我们此次实在上一次作业的基础上,实现动作与游戏场景的分离,所以我们要在源代码的基础上,分离一个动作管理类(SSAction)出来,在动作管理类中,管理所有动作。动作管理类的实现,均按照课间代码方式,所以下面主要po出来具体实现代码和与上一次不同代码;

FirstSceneActionManagerSS

FirstSceneActionManagerSS是动作控制器,它继承了动作管理基类和SSActionCallback接口,实现人物和船的移动的调用

public class FirstSceneActionManager : SSActionManager, SSActionCallback
{
    public SSActionEventType Complete = SSActionEventType.Completed;


    public void BoatMove(BoatController Boat)
    {
        Complete = SSActionEventType.Started;
        CCMoveToAction action = CCMoveToAction.getAction(Boat.GetDestination(), Boat.GetMoveSpeed());
        addAction(Boat.GetGameObject(), action, this);
        Boat.ChangeState();
    }

    public void CharacterMove(Character GameObject, Vector3 Destination)
    {
        Complete = SSActionEventType.Started;
        Vector3 CurrentPos = GameObject.GetPosition();
        Vector3 MiddlePos = CurrentPos;
        if (Destination.y > CurrentPos.y)
        {
            MiddlePos.y = Destination.y;
        }
        else
        {
            MiddlePos.x = Destination.x;
        }
        SSAction action1 = CCMoveToAction.getAction(MiddlePos, GameObject.GetMoveSpeed());
        SSAction action2 = CCMoveToAction.getAction(Destination, GameObject.GetMoveSpeed());
        SSAction seqAction = CCSequenceAction.getAction(1, 0, new List<SSAction> { action1, action2 });
        this.addAction(GameObject.GetGameobject(), seqAction, this);
    }

    public void SSActionCallback(SSAction source)
    {
        Complete = SSActionEventType.Completed;
    }
}
新增动作事件接口
public enum SSActionEventType : int { Started, Completed }

    public interface SSActionCallback
    {
        void SSActionCallback(SSAction source);
    }
SSACTION动作管理类

包含了动作管理基类 – SSActionManager顺序动作组合类实现CCSequenceAction简单动作实现CCMoveToAction动作基类(SSAction),代码均使用课件上所给代码

  • 动作管理基类 SSActionManager:使用AddAction将游戏对象和动作绑定,并实现动作的自动回收
public class SSActionManager : MonoBehaviour
{
    private Dictionary<int, SSAction> actions = new Dictionary<int, SSAction>();
    private List<SSAction> waitingToAdd = new List<SSAction>();
    private List<int> watingToDelete = new List<int>();

    protected void Update()
    {
        foreach (SSAction ac in waitingToAdd)
        {
            actions[ac.GetInstanceID()] = ac;
        }
        waitingToAdd.Clear();

        foreach (KeyValuePair<int, SSAction> kv in actions)
        {
            SSAction ac = kv.Value;
            if (ac.destroy)
            {
                watingToDelete.Add(ac.GetInstanceID());
            }
            else if (ac.enable)
            {
                ac.Update();
            }
        }

        foreach (int key in watingToDelete)
        {
            SSAction ac = actions[key];
            actions.Remove(key);
            Object.Destroy(ac);
        }
        watingToDelete.Clear();
    }

    public void addAction(GameObject gameObject, SSAction action, SSActionCallback ICallBack)
    {
        action.gameObject = gameObject;
        action.transform = gameObject.transform;
        action.CallBack = ICallBack;
        waitingToAdd.Add(action);
        action.Start();
    }
}
  • 动作基类(SSAction):创建了动作的基本元素,继承ScriptableObject类,不需要绑定具体的游戏对象的可编程类;
public class SSAction : ScriptableObject
{

    public bool enable = true;
    public bool destroy = false;

    public GameObject gameObject;
    public Transform transform;
    public SSActionCallback CallBack;

    public virtual void Start()
    {
        throw new System.NotImplementedException();
    }

    public virtual void Update()
    {
        throw new System.NotImplementedException();
    }
}

  • 简单动作实现CCMoveToAction:实现物体的运动,就像非动作分离版本中的move类功能相似
public class CCMoveToAction : SSAction
{
    public Vector3 target;
    public float speed;

    private CCMoveToAction() { }
    public static CCMoveToAction getAction(Vector3 target, float speed)
    {
        CCMoveToAction action = ScriptableObject.CreateInstance<CCMoveToAction>();
        action.target = target;
        action.speed = speed;
        return action;
    }

    public override void Update()
    {
        this.transform.position = Vector3.MoveTowards(transform.position, target, speed * Time.deltaTime);
        if (transform.position == target)
        {
            destroy = true;
            CallBack.SSActionCallback(this);
        }
    }

    public override void Start()
    {

    }
}
  • 顺序动作组合类实现CCSequenceAction:实现动作的顺序实现,也是原本版本中的Move类中的函数的分离实现
public class CCSequenceAction : SSAction, SSActionCallback
{
    public List<SSAction> sequence;
    public int repeat = 1; // 1->only do it for once, -1->repeat forever
    public int currentActionIndex = 0;

    public static CCSequenceAction getAction(int repeat, int currentActionIndex, List<SSAction> sequence)
    {
        CCSequenceAction action = ScriptableObject.CreateInstance<CCSequenceAction>();
        action.sequence = sequence;
        action.repeat = repeat;
        action.currentActionIndex = currentActionIndex;
        return action;
    }

    public override void Update()
    {
        if (sequence.Count == 0) return;
        if (currentActionIndex < sequence.Count)
        {
            sequence[currentActionIndex].Update();
        }
    }

    public void SSActionCallback(SSAction source)
    {
        source.destroy = false;
        this.currentActionIndex++;
        if (this.currentActionIndex >= sequence.Count)
        {
            this.currentActionIndex = 0;
            if (repeat > 0) repeat--;
            if (repeat == 0)
            {
                this.destroy = true;
                this.CallBack.SSActionCallback(this);
            }
        }
    }

    public override void Start()
    {
        foreach (SSAction action in sequence)
        {
            action.gameObject = this.gameObject;
            action.transform = this.transform;
            action.CallBack = this;
            action.Start();
        }
    }

    void OnDestroy()
    {
        foreach (SSAction action in sequence)
        {
            Object.Destroy(action);
        }
    }
}
Judge类

Judge类其实就是上个版本作业中的checkGameover函数的分离出来,单独作为一个脚本

public class Judge : MonoBehaviour
{
    CoastController fromCoast;
    CoastController toCoast;
    public BoatController boat;

    public Judge(CoastController c1,CoastController c2, BoatController b)
    {
        fromCoast = c1;
        toCoast = c2;
        boat = b;
    }

    public int Check()
    {   // 0->not finish, 1->lose, 2->win
        int from_priest = 0;
        int from_devil = 0;
        int to_priest = 0;
        int to_devil = 0;

        int[] fromCount = fromCoast.GetobjectsNumber();
        from_priest += fromCount[0];
        from_devil += fromCount[1];

        int[] toCount = toCoast.GetobjectsNumber();
        to_priest += toCount[0];
        to_devil += toCount[1];

        if (to_priest + to_devil == 6)      // win
            return 2;

        int[] boatCount = boat.GetobjectsNumber();
        if (boat.get_State() == -1)
        {   // boat at toCoast
            to_priest += boatCount[0];
            to_devil += boatCount[1];
        }
        else
        {   // boat at fromCoast
            from_priest += boatCount[0];
            from_devil += boatCount[1];
        }
        if (from_priest < from_devil && from_priest > 0)
        {       // lose
            return 1;
        }
        if (to_priest < to_devil && to_priest > 0)
        {
            return 1;
        }
        return 0;           // not finish
    }
}
控制器

将原本在Director类中的CharacterController控制器,BoatController控制器和LandController控制器拆分成单独的文件,并且删除Move控制器,因为被分离后的动作管理基类替代。

GUI

与原先版本基本相同,还是实现Label显示和Button的Restart功能和计时器功能

2.3 效果展示

  • 添加了天空盒
    在这里插入图片描述
    在这里插入图片描述

2.4 仓库地址

gitee仓库

三、材料与渲染联系

3.1 Standard Shader 自然场景渲染器相关效果的实现

  • 首先我们进行金属关泽属性的渲染工作,我们首先新建5个material,均使用RGB(200,80,80)创建,之后使用不同的程度的渲染,查看它们之间的区别

未进行渲染图:
在这里插入图片描述

首先尝试使用不同的金属光泽来更改着色器,通过Shader中的Metallic来实现:
在这里插入图片描述
从左到右金属光泽程度分别为:0、0.25、0.5、0.75、1.0

在这里插入图片描述
我们可以看到,随着金属光泽程度的加深,物体的反光性更高,并且颜色加深,类似于在原材质上方涂了一层包浆。

尝试使用不同的平滑程度来渲染小球:
在这里插入图片描述
从左到右平滑程度为:0、0.25、0.5、0.75、1.0

在这里插入图片描述
我们可以看到,随着平滑度的增加,材质从类似于磨砂的不反光材质,变成了全反光的类似与玻璃球的材质

最后尝试一下镜面模式,首先要更换材质的类型为镜面模式,而非金属模式
在这里插入图片描述
我们选择几个不同的颜色镜面,最右侧两个选择相同颜色,光滑度不同的镜面

在这里插入图片描述
我们可以看到,镜面类似于在物体上添加了一个对应颜色的反光镜,最后的呈现颜色是材质颜色和镜面颜色的混合。

3.2 用博客给出游戏中利用 Reverb Zones 呈现车辆穿过隧道的声效的案例

我们首先加入Audio Reverb Zone和Audio Source。
在这里插入图片描述
我们设定相机位置(0,0,0),所以设置音源位置为(-5,0,-12),这样我们的距离音源为13,我们的ReverbZone的最小距离为10,最大距离15,所以13位于梯度混响区;
在这里插入图片描述
我们这里将Reverb Preset设置为Cave,并为Audio Source加入一段音源(Asset Store中下载),之后这只音源为Loop,即可开始播放(这里没有找到开车音源,用了一段警笛音源代替)
在这里插入图片描述

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值