跑酷类小demo

来源:互联网 发布:windows连接git服务器 编辑:程序博客网 时间:2024/06/04 22:22

原文地址:http://blog.csdn.net/sun15980/article/details/51093048

------------------------------------------------------------------

在两个月前曾写了一篇《【Unity3D实战】零基础一步一步教你制作跑酷类游戏(1)》,里面一步一步演示了制作跑酷类游戏,然而由于时间原因,只写到了让角色往前移动为止。这个坑一直没有时间去填,(虽然也没多少人看啦),今天刚好有时间完成了一个跑酷类游戏的Demo。放上来给有兴趣的朋友看看。

Demo源码及对应素材下载:链接:http://pan.baidu.com/s/1i4QkkuD 密码:p04w

游戏简要说明

游戏类型:跑酷类游戏(Demo,非完整游戏)

操作方式:左右方向键(可自己移植到手机端)

游戏要素:

1.游戏角色会自动向前跑,玩家可通过左右方向键让其左右移动

2.游戏中存在障碍物,玩家需避开这些障碍物,否则会因为被障碍物阻挡的原因无法前进

3.当游戏角色因为被阻挡而消失在视野中时,视为失败

4.当游戏角色因为被阻挡而处于偏后方时,会提高移动速度直到回到原本所处的屏幕位置

游戏场景搭建

使用准备好的素材(路面、人物、障碍物),将这些素材制作成Prefab,然后根据自己喜好搭建好场景(如何搭建请看上一篇教程:《【Unity3D实战】零基础一步一步教你制作跑酷类游戏(1)》)。如下图:

游戏脚本编写

游戏角色控制器moveController:
新建一个C#文件,命名为moveController,然后将其打开。
由于角色需要向前、左、右三个方向移动,所以我们需要有其在前进方向上的速度左右方向上的速度,分别命名为:moveVSpeed、moveHSpeed,
同时由于玩家在落后的情况下需要加速,所以我们声明两个变量:前进方向上的最小移动速度minVSpeed前进方向上的最大移动速度maxVSpeed
于是我们可以得到以下脚本:
[csharp] view plain copy
  1. // 前进移动速度  
  2. float moveVSpeed;  
  3. // 水平移动速度  
  4. public float moveHSpeed = 5.0f;  
  5. // 最大速度  
  6. public float maxVSpeed = 10.0f;  
  7. // 最小速度  
  8. public float minVSpeed = 5.0f;  
其中moveHSpeed、maxVSpeed、minVSpeed声明为public,方便在面板上修改。

错误修改:感谢 jewis123 朋友提出的,这里漏了jumpHeight与m_jumpState的定义,前者代表最大高度,后者代表当前是向上跳跃,还是从高处落下,详细可查看源码。

接下来,在Start()函数中定义moveVSpeed的初始值:
        moveVSpeed = minVSpeed;
在Update()中使人物能移动起来:
[csharp] view plain copy
  1. float h = Input.GetAxis("Horizontal");  
  2. Vector3 vSpeed = new Vector3(this.transform.forward.x, this.transform.forward.y, this.transform.forward.z) * moveVSpeed ;  
  3. Vector3 hSpeed = new Vector3(this.transform.right.x, this.transform.right.y, this.transform.right.z) * moveHSpeed * h;  
  4. Vector3 jumpSpeed = new Vector3(this.transform.up.x, this.transform.up.y, this.transform.up.z) * jumpHeight * m_jumpState;  
  5. this.transform.position += (vSpeed + hSpeed + jumpSpeed) * Time.deltaTime;  
保存一下cs文件,切换到Unity,将该脚本挂载在角色对象的身上,保留默认值或手动设置:


运行游戏,看看是否能成功跑起来,并且能通过左右键控制人物左右移动。

看着人物越跑越远越跑越远,最后消失在远方......诶!教练,这和说好的不一样啊!人物咋不见了?

咳咳,这是因为我们没有让摄像机跟随它的原因,接下来,我们让摄像机与人物一起移动~

打开刚才的C#文件,声明一个public的变量

    // 摄像机位置
    public Transform cameraTransform;

在Update()函数中,添加以下代码:

// 设置摄像机移动速度
Vector3 vCameraSpeed = new Vector3(this.transform.forward.x, this.transform.forward.y, this.transform.forward.z) * minVSpeed;
// 让摄像机跑起来
cameraTransform.position += (vCameraSpeed) * Time.deltaTime;

注意到没,这里我们所定义的摄像机的移动速度与人物移动速度有点差别:
1.摄像机没有左右移动
2.摄像机的速度恒定为minVSpeed,也就是我们所定义的人物的最小移动速度(当然这个时候人物也一直是以这个速度在移动)

转到Unity,查看人物身上的Move Controller组件,现在这里应该多了一个变量等你设置:

我们将摄像机拖动到camera Transform处,再运行游戏。这时候你应该能看到人物在不断往前走,但在屏幕上的位置是没有变化的,因为摄像机一起移动了。

人物走着走着 哎呀 前面怎么没路了?别急,让我们来让路无限延长起来~

首先我们将道路的GameObject复制几个,我这里是总共有3个道路的GameObject,分别命名为Road1,Road2,Road3

然后在每一个Road下,添加一个Cube,将Cube的Mesh Renderer关闭,并将其Box Collider的Is Trigger勾上。命名为ArrivePos。(我才不会告诉你们这一步应该在上一行之前做呢!)

多条道路拼好,连成一条笔直的公路。

然后新建一个空物体,命名为GameManager,为其新建C#Script  GameManager.cs,然后打开该脚本。

声明一下多个变量:(注意引用命名空间using System.Collections.Generic;
[csharp] view plain copy
  1. // 生成障碍物点列表  
  2. public List<Transform> bornPosList = new List<Transform>();  
  3. // 道路列表  
  4. public List<Transform> roadList = new List<Transform>();  
  5. // 抵达点列表  
  6. public List<Transform> arrivePosList = new List<Transform>();  
  7. // 障碍物列表  
  8. public List<GameObject> objPrefabList = new List<GameObject>();  
  9. // 目前的障碍物  
  10. Dictionary<string, List<GameObject>> objDict = new Dictionary<string, List<GameObject>>();  
  11. // 道路间隔距离  
  12. public int roadDistance;  
并定义函数:
[csharp] view plain copy
  1. // 切出新的道路  
  2. public void changeRoad(Transform arrivePos)  
  3. {  
  4.     int index = arrivePosList.IndexOf(arrivePos);  
  5.     if(index >= 0)  
  6.     {  
  7.         int lastIndex = index - 1;  
  8.         if (lastIndex < 0)  
  9.             lastIndex = roadList.Count - 1;  
  10.         // 移动道路  
  11.         roadList[index].position = roadList[lastIndex].position + new Vector3(roadDistance, 0, 0);  
  12.   
  13.         initRoad(index);  
  14.     }  
  15.     else  
  16.     {  
  17.         Debug.LogError("arrivePos index is error");  
  18.         return;  
  19.     }  
  20. }  
  21.   
  22. void initRoad(int index)  
  23. {  
  24.       
  25.     string roadName = roadList[index].name;  
  26.     // 清空已有障碍物  
  27.     foreach(GameObject obj in objDict[roadName])  
  28.     {  
  29.         Destroy(obj);  
  30.     }  
  31.     objDict[roadName].Clear();  
  32.   
  33.     // 添加障碍物  
  34.     foreach(Transform pos in bornPosList[index])  
  35.     {  
  36.         GameObject prefab = objPrefabList[Random.Range(0, objPrefabList.Count)];  
  37.         Vector3 eulerAngle = new Vector3(0, Random.Range(0, 360), 0);  
  38.         GameObject obj = Instantiate(prefab, pos.position, Quaternion.EulerAngles(eulerAngle)) as GameObject;  
  39.         obj.tag = "Obstacle";  
  40.         objDict[roadName].Add(obj);  
  41.     }  
  42. }  

在Start()中:
[csharp] view plain copy
  1. void Start () {  
  2.     foreach(Transform road in roadList)  
  3.     {  
  4.         List<GameObject> objList = new List<GameObject>();  
  5.         objDict.Add(road.name, objList);  
  6.     }  
  7.     initRoad(0);  
  8.     initRoad(1);  
  9. }  
然后打开之前的moveController.cs,声明变量:
    // 游戏管理器
    public GameManager gameManager;
定义函数:
[csharp] view plain copy
  1. void OnTriggerEnter(Collider other)  
  2. {  
  3.     // 如果是抵达点  
  4.     if (other.name.Equals("ArrivePos"))  
  5.     {  
  6.         gameManager.changeRoad(other.transform);  
  7.     }  
  8.     // 如果是透明墙  
  9.     else if (other.tag.Equals("AlphaWall"))  
  10.     {  
  11.         // 没啥事情  
  12.     }  
  13.     // 如果是障碍物  
  14.     else if(other.tag.Equals("Obstacle"))  
  15.     {  
  16.   
  17.     }  
  18. }  
  呼,一大串代码,大家敲的累不累,什么!你是copy过去的?太过分了!我要拿刀子了!

嗯,切换回Unity中,点击GameManager这个物体,设置其GameManager组件的值:


这里的BornPos指的是障碍物出生点,以下图所示为每一条道路定义一个或多个出生点,每条路的出生点用一个BornPos的空物体进行管理:
然后将出生点按其所处道路的序号一一拖入(先设置size的值,3条就设置为3)
RoadList也是一样,将道路按序号一一拖入。

这里的ArrivePosList要注意一下,并不是直接按道路序号拖入,而是往后一位,即:
road2
road3
........
roadn
road1
这样的顺序将其对应的ArrivePos拖入列表

然后将需要生成的障碍物的Prefab文件拖入ObjPrefabList
设置道路的间隔距离(即一条道路的中心点到接下来一条道路中心点的距离 distance = road1.length / 2 + road2.length / 2 大概这么计算)

到这一步为止,GameManager的设置基本完成。点击人物的GameObject,设置moveController,将GameManager的游戏对象拖入到指定位置:


对了,还有一步非常重要的设置:
为人物添加Collider与RightBody,为所有障碍物和路面添加Collider(注意不要勾上Is Trigger)

然后运行游戏。

呼,这时候没有问题的话应该是能看到有障碍物出现了,人物走到障碍物处会被挡住,并且道路会自动拼接移动,无止境的走下去、走下去、走下去。。。

这个Demo也基本进入尾声了,接下来,做最后的游戏失败判断和让角色赶回正常位置。

打开GameManager.cs,声明变量:

    public bool isEnd = false;

打开moveController.cs 声明变量:

    // 摄像机距离人物的距离
    public float cameraDistance;

在Update()函数中 添加以下代码:
[csharp] view plain copy
  1. // 当人物与摄像机距离小于cameraDistance时 让其加速  
  2.         if(this.transform.position.x - cameraTransform.position.x < cameraDistance)  
  3.         {  
  4.             moveVSpeed += 0.1f;  
  5.             if (moveVSpeed > maxVSpeed)  
  6.             {  
  7.                 moveVSpeed = maxVSpeed;  
  8.             }  
  9.         }  
  10.         // 超过时 让摄像机赶上  
  11.         else if(this.transform.position.x - cameraTransform.position.x > cameraDistance)  
  12.         {  
  13.             moveVSpeed = minVSpeed;  
  14.             cameraTransform.position = new Vector3(this.transform.position.x - cameraDistance, cameraTransform.position.y, cameraTransform.position.z);  
  15.         }  
  16.         // 摄像机超过人物  
  17.         if(cameraTransform.position.x - this.transform.position.x > 0.0001f)  
  18.         {  
  19.             Debug.Log("你输啦!!!!!!!!!!");  
  20.             gameManager.isEnd = true;  
  21.         }  

定义OnGUI()函数:
[csharp] view plain copy
  1. void OnGUI()  
  2.     {  
  3.         if (gameManager.isEnd)  
  4.         {  
  5.             GUIStyle style = new GUIStyle();  
  6.   
  7.             style.alignment = TextAnchor.MiddleCenter;  
  8.             style.fontSize = 40;  
  9.             style.normal.textColor = Color.red;  
  10.             GUI.Label(new Rect(Screen.width / 2 - 100, Screen.height / 2 - 50, 200, 100), "你输了~", style);  
  11.   
  12.         }  
  13.     }  

到这一步,Demo编写完成。(由于现在是凌晨4点 实在太疲惫,所以本篇基本都是直接贴代码,如果有朋友有什么问题的话 可以直接留言哈~)