SYSU-SSE 3D游戏编程与设计 学习笔记(2)--空间与运动

前言

中山大学软件工程学院 3D游戏编程与设计课程学习记录博客
游戏代码: 游戏代码

简答题

  1. 游戏对象运动的本质是什么
  • 游戏对象的运动过程本质上就是游戏对象transform属性中的空间位置(Position)、旋转角度(Rotation)、大小(Scale)三个属性随着时间在做某种特定的变化。
  1. 请用三种方法以上方法,实现物体的抛物线运动
  • 使用两个Script修改 this.transform.position ,一个负责物体的自由加速下落,一个负责物体垂直重力方向的匀速运动

    物体向右匀速运动

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

public class Moveright : MonoBehaviour
{
    private int speed = 10;
    // Start is called before the first frame update
    void Start()
    {
        
    }

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

物体自由下落

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

public class Fall : MonoBehaviour
{
    private float g = 9.8F;
    private float speed;
    // Start is called before the first frame update
    void Start()
    {
        speed = 0;
    }

    // Update is called once per frame
    void Update()
    {
        speed += g * Time.deltaTime;
        this.transform.position += Vector3.down * Time.deltaTime * speed;
    }
}
  • 使用一个Script调用 this.transform.Translate
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Parabola : MonoBehaviour
{
    private float g = 9.8F;
    private float speed;
    private float fallingSpeed;
    // Start is called before the first frame update
    void Start()
    {
        speed = 10;
        fallingSpeed = 0;
    }

    // Update is called once per frame
    void Update()
    {
        fallingSpeed += g * Time.deltaTime;
        this.transform.Translate(Vector3.left * speed * Time.deltaTime);
        this.transform.Translate(Vector3.down * fallingSpeed * Time.deltaTime);
    }
}
  • 调用 Mathf.MoveTowards 获取下一个位置的坐标并赋值给 this.transform.position
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Fall3 : MonoBehaviour
{
    private float g = 9.8F;
    private float speed;
    private float falling_speed;
    // Start is called before the first frame update
    void Start()
    {
        speed = 10;
        falling_speed = 0;
    }

    // Update is called once per frame
    void Update()
    {
        falling_speed += g * Time.deltaTime;
        float target_y = Mathf.MoveTowards(this.transform.position.y, this.transform.position.y - falling_speed * Time.deltaTime, speed * Time.deltaTime);
        float target_x = Mathf.MoveTowards(this.transform.position.x, this.transform.position.x + speed * Time.deltaTime, falling_speed * Time.deltaTime);
        this.transform.position = new Vector3(target_x, target_y, this.transform.position.z);
    }
}
  1. 写一个程序,实现一个完整的的太阳系,其他星球围绕太阳的转速必须不一样,且不在一个法平面
  • 脚本代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SolarSystem : MonoBehaviour
{
    public Transform Sun;
    public Transform Mercury;
    public Transform Venus;
    public Transform Earth;
    public Transform Moon;
    public Transform Mars;
    public Transform Jupiter;
    public Transform Saturn;
    public Transform Uranus;
    public Transform Neptune;
    // Start is called before the first frame update
    void Start()
    {
        init_planetScale();
        init_planetPosition();
    }

    // Update is called once per frame
    void Update()
    {
        Mercury.RotateAround(Sun.position, new Vector3(0.3f, 1, 0), 10 * 365 / 87.7f * Time.deltaTime);
        Venus.RotateAround(Sun.position, new Vector3(0.2f, 1, 0), 10 * 365 / 224.7f * Time.deltaTime);
        Earth.RotateAround(Sun.position, Vector3.up, 10 * Time.deltaTime);
        Earth.Rotate(Vector3.up * 30 * Time.deltaTime);
        Moon.RotateAround(Earth.position, Vector3.up, 365 * Time.deltaTime);
        Mars.RotateAround(Sun.position, new Vector3(0.5f, 1, 0), 10 * 365 / 686.98f * Time.deltaTime);
        Jupiter.RotateAround(Sun.position, new Vector3(0.5f, 1, 0), 10 * 1 / 11.8f * Time.deltaTime);
        Saturn.RotateAround(Sun.position, new Vector3(0.6f, 1, 0), 10 * 1 / 29.5f * Time.deltaTime);
        Uranus.RotateAround(Sun.position, new Vector3(0.23f, 1, 0), 10 * 1 / 80.4f * Time.deltaTime);
        Neptune.RotateAround(Sun.position, new Vector3(0.17f, 1, 0), 10 * 1 / 164.8f * Time.deltaTime);
    }

    void init_planetScale()
    {
        this.Sun.localScale = new Vector3(20, 20, 20);
        this.Mercury.localScale = new Vector3(1.5F, 1.5F, 1.5F);
        this.Venus.localScale = new Vector3(3.6F, 3.6F, 3.6F);
        this.Earth.localScale = new Vector3(3.6F, 3.6F, 3.6F);
        this.Moon.localScale = new Vector3(0.9F, 0.9F, 0.9F);
        this.Mars.localScale = new Vector3(2.1F, 2.1F, 2.1F);
        this.Jupiter.localScale = new Vector3(14, 14, 14);
        this.Saturn.localScale = new Vector3(12, 12, 12);
        this.Uranus.localScale = new Vector3(5, 5, 5);
        this.Neptune.localScale = new Vector3(5, 5, 5);
    }
    void init_planetPosition()
    {
        this.Sun.position = Vector3.zero;
        this.Mercury.position = new Vector3(24, 0.2f, 0.02f);
        this.Venus.position = new Vector3(32, 0.03f, 0.3f);
        this.Earth.position = new Vector3(40, 0.07f, 0.7f);
        this.Moon.position = new Vector3(45, 0.07f, 0.7f);
        this.Mars.position = new Vector3(60, 0.09f, 0.9f);
        this.Jupiter.position = new Vector3(80, 0.8f, 0.08f);
        this.Saturn.position = new Vector3(100, 0.06f, 0.6f);
        this.Uranus.position = new Vector3(120, 0.3f, 0.39f);
        this.Neptune.position = new Vector3(140, 0.7f, 0.79f);
    }
}
  • 在菜单栏选择 GameObject ,选择 Create Empty, 将脚本搭载到新建的游戏对象中,再使用 GameObject3D Object 中的 Sphere 新建多个球体来代表行星,并在Unity的Asset Store下载了一个 行星的资源包,并将行星搭载到脚本上
  • 运行结果

编程实践

牧师与魔鬼

游戏代码: 游戏代码
游戏演示视频: 演示视频

目录结构

Resources 项目用到的天空盒和预设等
Scripts 项目的脚本

编程内容

  • 将游戏对象做成预制
  • 使用MVC结构

实现思路

  • 组织游戏资源
    Assets/Resources/Prefabs 目录下储存预制好的游戏对象

  • MVC框架设计

  1. 场景中的所有GameObject就是Model,它们受到Controller的控制,比如说牧师和魔鬼受到MyCharacterController类的控制,船受到BoatController类的控制,河岸受到CoastController类的控制。
  2. View就是UserGUI和ClickGUI,它们展示游戏结果,并提供用户交互的渠道(点击物体和按钮)。
  3. 除了刚才说的MyCharacterController、BoatController、CoastController以外,还有更高一层的Controller:FirstController(场景控制器),FirstController控制着这个场景中的所有对象,包括其加载、通信、用户输入。
    最高层的Controller是Director类,一个游戏中只能有一个实例,它控制着场景的创建、切换、销毁、游戏暂停、游戏退出等等最高层次的功能。
  • SSDirector
    利用单例模式创建导演,一个游戏导演只能有一个,这里继承于System.Object,保持导演类一直存在,不被Unity内存管理而管理,导演类类似于生活中的导演,安排场景,场景切换,都是靠它来指挥。
  • UserGUI
    建立用户的交互界面,比如按钮和标签
  • ClickGUI
    检测船和角色是否被点击
    点击则触发接口中的动作。然后进入控制器进行对应的函数操作。
  • conInterface
    这是将所有的接口放在同一命名空间下,同样方便了其他模块调用此命名空间。分别是场景控制器的接口,利用这个接口,得知当前场景是由哪个控制,然后向场景控制器传达要求,以及用户动作的接口,用户通过键盘、鼠标等对游戏发出指令,这个指令会触发游戏中的一些行为,由IUserAction来声明。
  • LandModel
    用于控制与河岸有关的动作,比如角色上下岸,船的离开和停靠。
    陆地的属性:陆地有两块,一个标志位来记录是开始的陆地还是结束陆地,陆地的位置,以及陆地上的角色,每个角色的位置
  • RoleModel
    用于控制6个角色的动作,比如上船,上岸等。
    一个角色的属性:标志角色是牧师还是恶魔,标志是否在船上
    角色模型的函数:去到陆地/船上(其实就是把哪个作为父节点,并且修改是否在船上标志),其他就是基本的get/set函数。
  • BoatModel
    用于控制船的运动以及角色的上下船绑定。
    船的属性:船在开始/结束陆地旁的位置,在开始/结束陆地旁船上可以载客的两个位置(用Vector3的数组表示),船上载有的角色(用角色模型的数组来记录),标记船在开始陆地还是结束陆地的旁边。
  • Controller
    这是一个控制器,对场景中的具体对象进行操作,可以看到这个控制器继承了两个接口类并实现了它们的方法,控制器是场景中各游戏对象行为改变的核心。他需要引用模型控制器以及接口的命名空间来调用其中实现的函数来达到控制的目的。
  • 游戏演示
    在Unity创建空对象并将Controller搭载上去并运行


最后感谢参考博客

posted @ 2022-10-24 17:26  joshf  阅读(102)  评论(0编辑  收藏  举报