2019-09-18

空间与运动

本文总阅读量

一. 简答并用程序验证【建议做】

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

    游戏对象的运动,实质上是游戏对象相对于原点位置的改变,或者相对于其他游戏对象位置的改变,通过transform组件中的position属性数值的改变可以实现游戏对象的运动。

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

    1. 直接改变Transform的Position

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
        public float Vx;
      public float Vy;

      private float nowVy;
      private Transform originTransform;

      // Use this for initialization
      void Start () {
      nowVy = Vy;
      originTransform = this.transform;
      }

      // Update is called once per frame
      void Update () {
      if (nowVy+Vy > 0.00001) {
      this.transform.position += Vector3.up * Time.deltaTime * nowVy;
      this.transform.position += Vector3.left * Time.deltaTime * Vx;
      nowVy -= 10 * Time.deltaTime;
      } else {

      }
      }
      1. 通过使用Vector3.MoveTowards

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
         // Update is called once per frame
        void Update () {
        Debug.Log (nowVy + Vy);
        if (nowVy+Vy > 0.00001) {
        Vector3 target = this.transform.position + Vector3.up * Time.deltaTime * nowVy + Vector3.left * Time.deltaTime * Vx;
        this.transform.position = Vector3.MoveTowards (this.transform.position, target, Time.deltaTime);
        nowVy -= 10 * Time.deltaTime;
        } else {

        }
        }
      2. 通过使用transform.Translate

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
         public float Vx;
        public float Vy;

        private float nowVy;
        private Vector3 speed;
        private Vector3 Gravity;

        // Use this for initialization
        void Start () {
        Gravity = Vector3.zero;
        speed = new Vector3 (Vx, Vy, 0);
        }

        // Update is called once per frame
        void Update () {
        if (2*Vy+Gravity.y > 0.00001) {
        Gravity.y -= 10 * Time.fixedDeltaTime;
        this.transform.Translate (speed*Time.fixedDeltaTime);
        this.transform.Translate (Gravity*Time.fixedDeltaTime);
        } else {

        }
        }

写一个程序,实现一个完整的太阳系,其他星球围绕太阳转速必须不一样,且不在一个法平面上

实现过程

创建游戏对象,并根据太阳系天体图片大致排列出各个星球的位置和大小

素材是上网找图片就好了
所有其他星球是sun的子类,moon又是earth的子类
创建script并挂载到sun上,将各个星球拖到对应的transform上

项目在Github

实现效果

二. 编程实践

阅读以下游戏脚本

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!

程序需要满足的要求

|  动作  | 规则  |
|  ----   | ---- |
| 开船  | 船停靠在岸边且穿上至少有一人 |
| 上船  | 船停靠在岸边,船上有空位且岸上有人 |
| 下船  | 船停靠在岸边且船上有人 |  
  • 请将游戏中对象做成预制
  • 在 GenGameObjects 中创建 长方形、正方形、球 及其色彩代表游戏中的对象。
  • 使用 C# 集合类型 有效组织对象
  • 整个游戏仅 主摄像机 和 一个 Empty 对象, 其他对象必须代码动态生成!!! 。 整个游戏不许出现 Find 游戏对象, SendMessage 这类突破程序结构的 通讯耦合 语句。 违背本条准则,不给分
  • 请使用课件架构图编程,不接受非 MVC 结构程序
  • 注意细节,例如:船未靠岸,牧师与魔鬼上下船运动中,均不能接受用户事件!

    实现过程

    做好预制(Priests,Devil,Water,Boat,Shore)
    可以先去做一些materials,去网上搜图片

    然后把对应的material拖到prefab的属性栏里

    创建scripts文件夹并编写代码

    目录结构如图所示

    代码写好后把对应的脚本拖到Scene的Object中,我把ClickGUI拖到了Main Camera下面,然后创建一个空对象并把FirstController拖到下面

实现代码

MVC架构

  • Model:游戏中的GameObject
  • View:UserGUI,Click GUI
  • Controller:SSDirector(最高级别的Controller),FirstController,以及其他基础的Controller(BoatController,MyCharacterController,ShoreController)

    游戏接口定义

    游戏中定义了两个接口类型,分别负责场景控制和用户交互,这两个接口不能直接实例化,而是要通过继承这两个接口来实现相应的功能
1
2
3
4
5
6
7
8
9
10
public interface SceneController{
void loadResources();
void pause();
void resume();}

public interface IUserAction{
void moveBoat();
void characterIsClicked(MyCharacterController characterCtrl);
void restart();
}

View定义

ClickGUI

ClickGUI用来检测用户的点击行为,并利用SceneController中的方法进行处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ClickGUI : MonoBehaviour
{
IUserAction action;
MyCharacterController characterController;

public void setController(MyCharacterController characterCtrl)
{
characterController = characterCtrl;
}

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

void OnMouseDown()
{
if (SSDirector.getInstance().state == State.START)
{
if (gameObject.name == "boat")
{
action.moveBoat();
}
else
{
action.characterIsClicked(characterController);
}
}

}
}
UserGUI

UserGUI 类用来显示游戏中的开始/暂停按钮、游戏状态和倒计时等元素,也是用户控制游戏开始和暂停的入口。

Controller定义

BoatController定义
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
readonly GameObject boat;//对象
readonly Moveable moveableScript;//控制移动
readonly Vector3 fromPosition = new Vector3(5, 1, 0);//船上的能坐人的位置,右边
readonly Vector3 toPosition = new Vector3(-5, 1, 0);//船上的能做人的位置,左边
readonly Vector3[] from_positions;
readonly Vector3[] to_positions;

// change frequently
int to_or_from; // to->-1; from->1 船在岸的左/右边
MyCharacterController[] passenger = new MyCharacterController[2];//船上的人物

//----------分割线---------------------
///<summary>
///船的移动,每移动一次改变标志位to_or_from
///</summary>
public void Move()
{
if (to_or_from == -1)
{
moveableScript.setDestination(fromPosition);
to_or_from = 1;
}
else
{
moveableScript.setDestination(toPosition);
to_or_from = -1;
}
}
//----------分割线---------------------
public int getEmptyIndex()
{
for (int i = 0; i < passenger.Length; i++)
{
if (passenger[i] == null)
{
return i;
}
}
return -1;
}

public bool isEmpty()
{
for (int i = 0; i < passenger.Length; i++)
{
if (passenger[i] != null)
{
return false;
}
}
return true;
}//获取船上的空位并判断是否没人
public Vector3 getEmptyPosition()
{
Vector3 pos;
int emptyIndex = getEmptyIndex();
if (to_or_from == -1)
{
pos = to_positions[emptyIndex];
}
else
{
pos = from_positions[emptyIndex];
}
return pos;
}//获取没人位置的坐标
//----------分割线---------------------
public void GetOnBoat(MyCharacterController characterCtrl)
{
int index = getEmptyIndex();
passenger[index] = characterCtrl;
}//人物对象上船
//----------分割线---------------------
public MyCharacterController GetOffBoat(string passenger_name)
{
for (int i = 0; i < passenger.Length; i++)
{
if (passenger[i] != null && passenger[i].getName() == passenger_name)
{
MyCharacterController charactorCtrl = passenger[i];
passenger[i] = null;
return charactorCtrl;
}
}
Debug.Log("Cant find passenger in boat: " + passenger_name);
return null;
}
//人物对象下船,得到下船人物的名字并返回它的控制器
//----------分割线---------------------
public int[] getCharacterNum()
{
int[] count = { 0, 0 };
for (int i = 0; i < passenger.Length; i++)
{
if (passenger[i] == null)
continue;
if (passenger[i].getType() == 0)
{ // 0->priest, 1->devil
count[0]++;
}
else
{
count[1]++;
}
}
return count;
}//获取船上个身份人物的数量
FirstController
  • 导入游戏资源,搭建游戏场景
  • 处理玩家请求(谁上船,开船,谁下船)
  • 监听游戏是否已经结束
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    //初始化游戏场景和配置
    void Awake()
    {
    SSDirector director = SSDirector.getInstance();
    director.currentSceneController = this;
    userGUI = gameObject.AddComponent<UserGUI>() as UserGUI;
    characters = new MyCharacterController[6];
    director.setFPS(60);
    director.leaveSeconds = director.totalSeconds;
    director.currentSceneController.loadResources();
    }
    ```

    ```c#
    //创建对象,导入各种预制体资源
    public void loadResources()
    {
    GameObject water = Instantiate(Resources.Load("Prefabs/Water",
    typeof(GameObject)), new Vector3(0, 0.5F, 0), Quaternion.identity, null) as GameObject;
    water.name = "water";
    //设置控制器
    fromShore = new ShoreController("from");
    toShore = new ShoreController("to");
    boat = new BoatController();
    //导入游戏人物对象并设置控制器
    for (int i = 0; i < 3; i++)
    {
    MyCharacterController cha = new MyCharacterController("priest");
    cha.setName("priest" + i);
    cha.setPosition(fromShore.getEmptyPosition());
    cha.getOnShore(fromShore);
    fromShore.getOnShore(cha);

    characters[i] = cha;
    }

    for (int i = 0; i < 3; i++)
    {
    MyCharacterController cha = new MyCharacterController("devil");
    cha.setName("devil" + i);
    cha.setPosition(fromShore.getEmptyPosition());
    cha.getOnShore(fromShore);
    fromShore.getOnShore(cha);

    characters[i + 3] = cha;
    }
    }
    ```
    ```c#
    //如果点了某个人物判断其是在船上还是在岸上,
    //如果在船上就上岸,如果在岸上就下船
    public void characterIsClicked(MyCharacterController characterCtrl)
    {
    if (characterCtrl.isOnBoat())
    {
    ShoreController whichShore;
    if (boat.get_to_or_from() == -1)
    { // to->-1; from->1
    whichShore = toShore;
    }
    else
    {
    whichShore = fromShore;
    }

    boat.GetOffBoat(characterCtrl.getName());
    characterCtrl.moveToPosition(whichShore.getEmptyPosition());
    characterCtrl.getOnShore(whichShore);
    whichShore.getOnShore(characterCtrl);

    }
    else
    { // character on shore
    ShoreController whichShore = characterCtrl.getShoreController();

    if (boat.getEmptyIndex() == -1)
    { // boat is full
    return;
    }

    if (whichShore.get_to_or_from() != boat.get_to_or_from()) // boat is not on the side of character
    return;

    whichShore.getOffShore(characterCtrl.getName());
    characterCtrl.moveToPosition(boat.getEmptyPosition());
    characterCtrl.getOnBoat(boat);
    boat.GetOnBoat(characterCtrl);
    }
    userGUI.status = check_game_over();
    }
    ```

    ```c#
    int check_game_over()
    { // 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 = fromShore.getCharacterNum();
    from_priest += fromCount[0];
    from_devil += fromCount[1];
    //获取左边对应的人数
    int[] toCount = toShore.getCharacterNum();
    to_priest += toCount[0];
    to_devil += toCount[1];
    //如果左边有六个人就是安全过河,即游戏胜利
    if (to_priest + to_devil == 6) // win
    return 2;
    //船上的人数目加上去
    int[] boatCount = boat.getCharacterNum();
    if (boat.get_to_or_from() == -1)
    { // boat at toShore
    to_priest += boatCount[0];
    to_devil += boatCount[1];
    }
    else
    { // boat at fromShore
    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
    }
其他具体的controller不再一一介绍

实现效果

视频链接

代码链接

Github