美文网首页
案例学习项目报告书

案例学习项目报告书

作者: 伊才妮 | 来源:发表于2018-08-06 16:21 被阅读0次

    一.项目设计方案
    二.实施过程分析
    三.项目的改进
    四.项目设计成果
    五.提升及展望

    一.项目设计方案

    • 本次学习的案例是一款轻松休闲游戏,名字叫做“跳一跳”。
    • 这个游戏是一个小人从台子上跳到另一个台子上,根据按下屏幕的时间可以控制小人跳跃的力度和远近,如果成功跳跃到台子上,则继续朝下一个台子进行跳跃,如果没有跳到台子上,则停止游戏,同时通过上传分数可以看到排名前十的成绩。
    • 主要是通过美术资源的设置,特效的设置,逻辑功能这三个方面的实现。

    二.实施过程分析

    根据“跳一跳”游戏的基本功能需求,相关实施过程如下:

    • 工程,角色及场景设置
    • 角色跳跃
    • 台子自动生成
    • 相机跟随
    • 死亡判定及重新开始游戏
    • 分数统计
    • 角色蓄力的粒子效果
    • 台子蓄力效果

    1. 工程,角色及场景设置

    • 创建舞台
      1. 在Hierarchy层级面板里创建一个立方体cube,按F2快捷键重命名为“Stage”作为小人进行跳跃的舞台。适当改其参数。
    • 创建地面
      1. 新建平面plane,重命名为“Ground”作为小人落下地面的承接物,
      2. 为了和台子有所区分,在Assets资产里建一个文件夹,取名叫“Materials”材质 用于放置材质球,建好材质球后拖到Ground地面上,修改参数,使其颜色有所区分。
      3. 会发现一个小问题,在修改完台子的相关参数后,发现台子陷入到地面里了,这是因为“cube”的中心轴在它的正中心,对Y轴进行调整,改为0.25。
      4. Alt+鼠标左键可以使场景中的物体进行旋转,便于观察。
    • 创建小人
      1. 新建一个游戏对象空物体,快捷键“ctrl+shift+N”,命名为“Player”同样的道理,轴心在物体的正中心,可以调整Y轴,使其正好落在台子上,在其作为父对象创建小人的头和身子,适当调整参数。
    • 保存
      1. 基本的场景建完后对场景进行保存,在“Assets”里新建“_Scenes”文件夹,用于存放场景,并进行保存,加下划线是为了让其排在第一个方便查找。

    2. 角色跳跃

    • 添加刚体组件

      1. 给“player”添加刚体组件“rigidbody”
    • 新建脚本

      1. 在“Assets”里新建“scripts”文件夹,目的是存放脚本,并新建“c# ”的脚本,
      2. 脚本的名字在代码里的类名需要一致才能流畅地进行代码运行。
    • 定义刚体组件
      在类里面定义一下刚体组件

      private Rigidbody _rigidbody;
      
    • 获取刚体组件
      在Start 方法里获取这个组件

      _rigidbody=GetComponent<Rigidbody>();
      
    • 思考
      跳跃的距离根据按下时长决定的,那么需要检测按下鼠标的动作,在update方法里进行检测:

      if (Input.GetMouseButtonDown(0)){ }//鼠标按下时返回值
      if (Input.GetMouseButtonUp(0)){ }//鼠标抬起时返回值
      if (Input.GetMouseButton(0)){ }//鼠标整个按下和抬起的返回值,可以多次返回
      
    • 标记开始时间
      在按下鼠标时标记一个开始时间,首先需要定义一个私有的成员的浮点型变量

      private float _startTime;
      

      然后在按下鼠标的函数里

      if (Input.GetMouseButtonDown(0)){ }
      

      对这个变量进行赋值

      _startTime = Time.time;
      
    • 计算按下时长
      在鼠标抬起时,计算总共按下鼠标的时长,在鼠标抬起的时候,利用抬起的时间减去按下时的时间得到一个流逝的时间

      var elapse = Time.time - _startTime;
      
    • 跳跃的方法
      定义一个跳跃的方法/函数

      void OnJump(float elapse){ }
      
      1. 括号里是参数,在鼠标抬起(button up)时调用这个函数,

      2. 在这个函数里,通过对刚体施加一个向上的力和一个向前的力,并且向前的力受到消逝时间和一个可以调整的参数的影响,以此达到通过控制按下时长来决定小人的跳跃距离。

      3. 这个函数所需要用到的可调整参数需要进行定义

        public float Factor;

      Public类型可以在代码外更改这个值。
      在OnJump函数内调用以下代码

      _rigidbody.AddForce(new Vector3(0, 5f, 0) + (_direction) * elapse * Factor,ForceMode.Impulse);
      

      “AddForce”的参数,第一个是作用的力 第二个默认是持续性的力
      ForceMode.Impulse指的是给物体添加一个瞬间的力并使用其质量,它是AddForce的四个重载之一

    • 调整重心

      1. 测试后发现小人重心不稳,是因为小人身体是胶囊的“Collider” 更改为“box collider” 特别注意的是 此时小人和台子的接触点为四个

      2. 根据物理学,小人会有重心,此时小人的重心偏高,需要用代码更改小人的重心,回到脚本进行更改

         _rigidbody.centerOfMass = new Vector3(0, 0, 0);
        
    • 调整摄像机

      1. 因为发现游戏视角里的场景并不利于玩家进行游戏
      2. 有一个技巧是,在“scene场景”里调好恰当的视角好按“ctrl+shif+F”。
      3. 然后在gameobject主导航里点击align with view 这样将游戏场景里的摄像机调整为和场景里一样的视角。

    3.台子自动生成

    • 基准盒子
      为了能让游戏持续进行下去,我们需要自动生成台子,我们需要一个基准盒子,然后需要按照随机距离和方向进行生成。

    • 定义相关参数
      定义一个盒子随机最远的距离的参数

      public float MaxDistance = 5;
      

      定义一个基准盒子物体

      public GameObject Stage;
      

      定义一个生成盒子的函数

      void SpawnStage(){ }
      
    • 生成台子
      那么生成的新的盒子就可以通生成的方法进行

      var stage = Instantiate(Stage);
      

      但生成的新的盒子的位置如何确定呢?我们需要定义一个当前台子以便调取当前台子物体的位置

      privateGameObject_currentStage;”
      

      在函数里写下

      stage.transform.position = _currentStage.transform.position + _direction * Random.Range(1.1f, MaxDistance);
      

      在start方法里写下

      _currentStage = Stage;   SpawnStage();
      

      将基准台子赋值到当天前台子,并调用生成台子的函数

    • 碰撞检测方法
      但此时无法形成随着小人的跳跃动态生成的效果,这是因为没有检测到小人的碰撞情况。

      当检测到小人跳到盒子上后,再次调用生成盒子的函数。用到刚体的一个碰撞检测方法

      void OnCollisionEnter(Collision collision){ }
      

      括号里的参数是指碰撞后的物体,此检测的原理是当物体的collider 或rigid body 碰到了另一个物体时进入开始调用

    • 区分碰撞
      小人碰撞的事件包含两个子事件,包含碰撞到盒子和里面这两个情况,怎么区分呢?通过

      collision.gameObject.name;
      

      来区分。
      需要用到条件语句,使小人只有在碰到当前的盒子,且当前的盒子不等于当前承接小人的盒子时,才会生成新的盒子,这样避免了小人碰到其他物体也生产,和小人在承接它的盒子上连续跳跃也生成的两个bug。具体通过条件语句进行判断。

    4.相机跟随

    • 目的
      实现小人跳到下一个台子后进行移动,而不是在跳的过程中进行移动

    • 相对位置
      需要获取相机相对小人的向量,也就是相对位置,当小人进行移动后,相机进行相对移动,相机移动到于之前小人相同视角的位置,也就是每次相机的移动都保证时以一个相对位置进行移动的。

      private Vector3 _cameraRelativePosition;
      

      设定一个相机的相对位置(向量)

    • 获取相机的相对位置
      怎么获取呢,在start方法里进行获取

      _cameraRelativePosition = Camera.main.transform.position - transform.position;
      

      就是相机的位置减去小人的位置

    • 相机移动
      定义一个新的函数,并在小人成功跳到新的盒子后进行调用

       void MoveCamera(){ };
      

      我们采用插件的形式让相机平滑地进行移动。当前相机位置加上一个相对位置就是相机移动后地位置

      Camera.main.transform.DOMove(transform.position + _cameraRelativePosition, 1);
      

      第一个参数是产生地移动,第二个参数是持续的时间,别忘了头命名进行引用

      using DG.Tweening;
      

      这个插件在官方软件里进行免费下载导入

    • 相机更改
      将透视改为正交,就不会产生近大远小了

    5.死亡判定及重新开始游戏

    • 结束游戏进程条件
      1. 当检测到小人落到地面或者小人不成功地跳跃,比如碰到了边界,或者小人自身不是直立状态时都会视为小人死亡,结束当前游戏进程。

      2. 这里为了让游戏更加完整,会定义一个新的游戏结束地方法函数,在符合小人死亡的情况下调用这个函数

        private void OnGameOver(){ };
        
      在这个函数里可以控制场景的切换。
    • 结构图及代码
      具体在这个方法里如何进行死亡判定呢,如下图所示:


      结构图手稿.png
    小人死亡示意图.png
    1. 特别注意的是,问号那里是指只要小人接触地面 仍会存在游戏失败的情况

    2. 在条件语句中,需要定义一个contacts的数组,用来代表碰撞的接触点,如果接触点是一个,用来判断是不是落在了台子的顶面,这里用到normal法线,因为及时接触点是一个,但落在了棱角上,法线方向就不是正上方

    3. 总之,只有判断是否接触点,且人物朝上才算符合加分的条件,否则游戏结束
      Length可以获取这个数组长度

       if (collision.gameObject.name == "Ground")
       {
           OnGameOver();
       }
       else
       {
         if (_currentStage != collision.gameObject)//条件 当前的小人所在的盒子不是碰撞后的游戏对象
         {
             var contacts = collision.contacts;//var定义数组变量contact 将被碰撞后的物体的接触点集(集合)赋值给这个数组变量
      
             //check if player's feet on the stage
             if (contacts.Length == 1 && contacts[0].normal == Vector3.up)//数组的长度就是这个接触集合是否为1(是否落到台子上),数组的法线是否是向上的(小人直立状态)
             {
                 _currentStage = collision.gameObject;//将当前的游戏对象赋值给当前的盒子
                 AddScore(contacts);
                 RandomDirection();
                 SpawnStage();
                 MoveCamera();
      
                 _enableInput = true;//可以继续进行点击鼠标
             }
             else // 虽然小人跳出去了,但可能由于跳偏了,或者小人的身子不是直立状态
             {
                 OnGameOver();
             }
         }
         else //still on the same box 仍在当前的盒子上跳
         {
             var contacts = collision.contacts;
      
             //check if player's feet on the stage
             if (contacts.Length == 1 && contacts[0].normal == Vector3.up)
             {
                 _enableInput = true;//还可以继续进行点击,也就是说允许在原地跳的
             }
             else // body just collides with this box
             {
                 OnGameOver();
             }
         }
       }
      
    • 场景变暗bug
      1. 这里进行检测时会发现游戏在跳转到另一个场景后会变暗,这是因为lighting下的setting中需要将 auto generate给关掉,让环境不自动生成一个光线资源 这样可以提高开发效率。

    6.分数统计

    • UI功能设置

      1. 这里需要用到“unity”中的UI功能,在“hierarchy”面板创建“Text” 用来显示分数,将场景编辑改为2D模式,方便对UI进行设置。
      2. 在“inspector” 面板里对“recttransform” 进行设置“anckors”(相当于将一个物体挂在固定的方位,当移动游戏场景时,固定在某个方位不变) 同时按住“alt” 会使ui物体也放置在相应的位置
    • 编写脚本
      定义一个私有成员的量,相当于申请空间

      public Text TotalScoreText;
      

      别忘了命名空间

      using UnityEngine.UI;
      

      否则会报错。记录分数需要一个变量进行记录

      private int _score;
      

      为了合理的管理并编辑积分系统,同时为了能达到如果准确度高的分数可以成倍增加,可以定义一个方法 函数

      private void AddScore(ContactPoint[] contacts){ }”
      
      1. 定义一个变量“hitPoint”,把“ ContactPoint[] contacts”里的数组的“point”赋值
        给“hitPoint”这个变量(point位置)。

      2. 使hitPoint的Y轴赋值0把三维的位置转化为二位的,同理, 把现在的台子的位置赋值给变量 stagePos , 让这个变量的Y轴也赋值成0

      3. 最后 定义一个变量 precision ,获取两点之间的距离 (hipPoint,stagePos)是小人碰撞的位置点到台子中心位置的距离

      4. 如果precision的值小于0.1 则进行两倍。

        _score += _lastReward;
        

        分数加等于一个值

        TotalScoreText.text = _score.ToString();
        

        将整型的值转为字符串,并在text里进行显示

    7.角色蓄力的粒子效果

    • 添加粒子系统

      1. 给游戏对象添加一个effect 里的particle system ,对这个粒子系统进行参数修改,粒子发生的起始形状做一下更改,
      2. 将初始的“cube”改为“hemisphere”半球的形状,并进行旋转。
      3. radius也就是半径也适当做一些更改,初始的速度改为负值,这样可以让 它可以往内部发射。
      4. Start size 开始的尺寸,范围。Start lifetime开始时间的时长。颜色调整。
    • 显示效果
      这个效果应该在什么时候才会显示出来呢?当按下鼠标进行蓄力时才会触发这个效果。这个需要在代码里进行实现。定义一个公有的游戏对象

      public GameObject Particle;
      

      然后在鼠标按下时让它的状态显示为真“ Particle.SetActive(true);”在抬起鼠标后状态关闭

      Particle.SetActive(false);
      
    • 去掉钩选
      初始的粒子效果状态设为“false”把钩选项去掉。

    8.角色蓄力效果

    • 分析
      为了产生使小人在点击鼠标后产生蓄力的效果,其实是对小人的身体进行延Y轴的缩放,并使小人的头部进行向下移动,这里用代码实现比较好,因为是需要根据按下鼠标的时长来确定蓄力效果的。

    • 定义变量
      获取小人的头和身体,需要定义两个共有的变量

      public Transform Head;”“public Transform Body;
      

      在botton的方法里进行使用这两个变量,区分按下 抬起 每次按鼠标这三个不同的方法。这里每一次按下鼠标都会产生蓄力所以在“Input.GetMouseButton(0)”里。

    • 调整缩放
      首先对身体进行改变

      Body.transform.localScale += new Vector3(1, -1, 1) * 0.05f * Time.deltaTime;
      

      获取身体的的形状属性进行加一个三维向量,x,z是正的,y是负的,乘以一个0.05的速率再乘上每秒刷新的时间
      然后对头进行改变

      Head.transform.localPosition += new Vector3(0, -1, 0) * 0.1f * Time.deltaTime;
      

      方法同前一个身体,只不过调取的是头的位置。

    • 还原
      蓄力结束后并没有复原,这时候就需要在鼠标抬起的函数里进行复原。以动画的形式进行复原,这里用到一个插件。首先,需要对小人的身体的“scale”和头的“position” 的原始参数有个了解。

      Body.transform.DOScale(0.1f, 0.2f);
      

      第一个参数也可以是一个三维向量,这里的一个值表示三个值都是一样的。后面的参数是动画播放的时间。

      Head.transform.DOLocalMoveY(0.29f, 0.2f);
      

      同理头部进行还原。这里的DOLocalMoveY指对Y轴进行移动。

    4.台子蓄力效果

    • 原理
      同小人的缩放,也需要用到插件,我们考虑到对台子的Y轴进行缩放,由于轴中心在台子的中间,所以需要对台子进行一个移动,以免出现穿帮。也是在按下鼠标时进行调用。其次要考虑的是对那个盒子进行缩放,前面以及定义了一个当前的盒子,可以直接用。

    • 代码

      _currentStage.transform.localScale += new Vector3(0, -1, 0) * 0.15f * Time.deltaTime;” 
      

      进行Y轴缩放

      _currentStage.transform.localPosition += new Vector3(0, -1, 0) * 0.15f * Time.deltaTime;”
      

      进行位置的移动

    • 还原
      同样需要进行还原

      _currentStage.transform.DOLocalMoveY(-0.25f, 0.2f);
      _currentStage.transform.DOScaleY(0.5f, 0.2f);”
      

    三.项目的改进

    • 台子随机生成的大小和颜色变换
    • 台子生成的随机方向功能,
    • 加分UI的的实时动态更新的漂浮效果
    • 成倍加分的功能
    • 联网排行榜功能的实现
    • 随机生成不同形状的台子
    • 小人翻转跳跃
    • 音频

    1.台子随机生成的大小和颜色变换

    • 大小随机变换
      改变台子的大小其实就是对x,z值进行改变,y不变。
      定义一个随机值

      var randomScale = Random.Range(0.5f, 1);
      

      它的范围是0.5-1,这样,这个随机值也就可以放在一个三维向量的x,z,上了。

      stage.transform.localScale = new Vector3(randomScale, 0.5f, randomScale);
      

      这里的localScale是相对于父级物体变换的缩放,定义一个“randomScale”
      是为了使盒子长和宽一致,以免出现细长的情况。

    • 颜色随机变换
      为了增加视觉效果,让台子的颜色随机变换,如何实现呢?我们需要获取台子的“renderer”的组件,并对“materials”里的“color”进行修改。

      stage.GetComponent<Renderer>().material.color = new Color(Random.Range(0f, 1), Random.Range(0f, 1), Random.Range(0f, 1));
      

      这里的Random.Range(0f, 1)里如果没有f,就会默认是整型,而且后面的一个数不会取值比如(0,1)其实只取了0这一个数,加上f后代表0-1之间的浮点数,不包含1 。

    2.台子生成的随机方向功能

    • 定义初始方向
      首先定义一个向量确定初始方向

      Vector3 _direction = new Vector3(1, 0, 0);
      

      这是一个朝x正方向的值。将生成盒子里的函数的普通向量改为这个定义的“_direction”通过变换这个“_direction”的x,z的值就可使盒子进行不同方向的生成。怎么去变换这个x,z的值呢?

    • 定义方法

      定义一个随机方向的方法

      void RandomDirection(){ };
      
    • 在这个里面进行判断
      这里主要通过首先定义一个种子,给它一个随机范围(0,2)这里其实只有0,1 这两个值,通过条件语句进行判断,从而实现方向的随机进行实现。

        var seed = Random.Range(0, 2);
      if (seed == 0)
      {_direction = new Vector3(1, 0, 0);}
      else
      {_direction = new Vector3(0, 0, 1);}”
      

      也可以用三元判断

      _direction = seed == 0 ? newVector3(1,0,0):newVector3(0,0,1);”
      
    • 小人的位置变换

      1. 台子的方向改变后,小人进行跳跃的方向也需要改变,

      2. 这时就需要在这个随机方法函数里,让小人的位置进行变换,

      3. 为了使旋转后的小人的朝向始终是一致的,这里需要用到transform.right功能,

      4. 可以想象这里的right就像是小人的右手,让小人始终朝right (朝红色的也就是x轴进行旋转,以保证朝向)把_direction赋值给这个transform.right

        transform.right = _direction;
        

    3.加分UI的的实时动态更新的漂浮效果

    • UI显示
      为了使加分显示。在canvas里新建一个text,为了和前面的总分相区分,改一下名称,分别是“TotalScore”和“Score”,然后调整Score的位置,在小人的周边位置。

    • 获取Text
      在代码里获取这个新建的text

      public Text SingleScoreText;
      
    • 定义
      方法只有在效得分才会让text里有内容显示,新定义一个方法,并在碰撞检测里进行调取。

      private void ShowScoreAnimation(){ }
      

      在这个函数里让实现分数的动态显示。

    • 获取实时位置

      1. 为了能达到实时飘分,并不受到摄像机的移动而改变飘分的位置便宜,我们需要得到小人的实时位置,
      2. 然后再通过打开和关闭text的显示状态,并将世界坐标系转到屏幕坐标系,然后用domove的动画方法让分数的Y轴方向加一个值,让它进行移动,
      3. 动画时长可以设定为1秒。
    • 定义变量
      具体代码里,首先定义一个成员变量,用来控制是否播放动画,

      _isUpdateScoreAnimation = true;
      

      前面别忘了定义一下bool值

      private bool _isUpdateScoreAnimation;
      

      bool默认是false

    • 实时飘分
      需要一个动画播放开始的时间,定义一个变量

      private float _scoreAnimationStartTime;”
      

      并让播放的时间为当前的时间,

        _scoreAnimationStartTime = Time.time;
      

      把bool值设为真

      _isUpdateScoreAnimation = true;”
      
    • 分数实时更新
      怎么让分数实时更新呢,并且让UI能获得小人的实时位置,以免因为相机的移动让UI产生相对运动,这里定义一个更新的方法

      void UpdateScoreAnimation(){ };”
      

      在这个方法里我们实现动态更新以及飘分的相关效果。并获取到小人的实时位置

    • 关闭飘分
      首先,我们需要让飘分动画播放一定时间后关闭,这里用到条件语句

      if (Time.time - _scoreAnimationStartTime > 1)
      _isUpdateScoreAnimation = false;
      
    • 获得实时位置

      1. 为了获得小人的实时位置,首先我们需要获取一个小人的当前屏幕所在的位置,

      2. 因为小人在三维空间里,我们需要把世界坐标系转化为屏幕坐标系

      3. 这里用到UI的工具箱功能(UI的transform组件都是rectTransform)
        里面有这样一个工具进行转换,转换时还需要获取渲染时摄像机的位置,获取相机的组件,世界坐标系是小人的位置,

      4. 转换好后 我们定义一个变量来接收转换好的屏幕坐标位置的返回值。

         var playerScreenPos=RectTransformUtility.WorldToScreenPoint(Camera.main,transform.position);
        
      5. 接下来就可以把飘分的text的位置设置为这个二维坐标位置。

    • 实现飘起效果

      1. 为了让分数实现飘起来的效果,我们用到了Vector2.Lerp的功能(已经转换到屏幕上了,所以是2),

      2. 初始位置到最终位置的一个线性移动(角色脚下位置到向Y轴偏移的一个向量位置)移动的时间就是当前时间减去动画开始播放的时间。

        SingleScoreText.color = Color.Lerp(Color.black, new Color(0, 0, 0, 0), Time.time - _scoreAnimationStartTime);”
        
    • 颜色渐变
      让分数在颜色上有一个渐变,也用到Lerp的功能,从黑色到透明。

      SingleScoreText.color = Color.Lerp(Color.black, new Color(0, 0, 0, 0), Time.time - _scoreAnimationStartTime);
      

      “new color”里的四个参数,前三个是RGB,最后一个是通道阿尔法,目的是为了产生透明效果。

    • 关闭初始显示
      在游戏编辑里把这个text关掉,可以运行检测一下。

    4.成倍加分的功能

    • 定义方法

      1. 首先需要定义一个新的加分方法

         private void AddScore(ContactPoint[] contacts){ };
        
      2. 碰撞集ContactPoint[]用来看小人接触面的点集,并用到条件语句进行判断是否符合成倍加分的条件,

      3. 即如果小人的位置和台子的位置之间的距离值的误差很小,就在原来加分的基础上再加一个控制值,这里需要定义一个整型的变量来控制最终得分

        private int _lastReward = 4;
        

      初始设置为4,用来替代之前的score+=;

    • 评估加分

      1. 在这个条件语句里怎么进行评估呢,首先需要保证接触数组里大于0,

      2. 然后获取这个接触点和当前台子的位置,

      3. 同时将这两个位置值的Y值设为0再来计算他们之间的距离,如果距离小于0.1,
        则执行加分的语句,让

         _lastReward = 2;
        
      4. 同理需要将整型转换为字符串进行显示,并显示飘分动画。

        if (contacts.Length > 0)
        {
           var hitPoint = contacts[0].point;
           hitPoint.y = 0;
        
           var stagePos = _currentStage.transform.position;
           stagePos.y = 0;
        
           var precision = Vector3.Distance(hitPoint, stagePos);
           if (precision < 0.1)
               _lastReward *= 2;
           else
               _lastReward = 1;
        
           _score += _lastReward;
           TotalScoreText.text = _score.ToString();
           ShowScoreAnimation();
        }
        
    • 注意事项
      别忘了,在udate方法里调用,

      if (_isUpdateScoreAnimation){
            UpdateScoreAnimation();
          }
      

    5.联网排行榜功能的实现

    • 前期准备

      1. 首先需要申请一个在线云服务器leancloud,
      2. 注册后可以免费使用一些功能,
      3. 创建新项目后会获得一个appid和appkey
      4. 下载leancloud unity sdk 并导入到unity里(不用导Engine.dll)
      5. 在leancloud的里面有相关文档,初始化,存储,等都有教程。
      6. 里面的代码可以直接用。
    • 初始化

      1. 按照官网上的指南
      2. 首先进行初始化
      3. hierarchy面板里创建游戏空物体
      4. 命名为leancloud,把导入的资源包里core底下的代码拖到这个空物体上
      5. 并把id和key复制过来
    • UI设置

      1. 思考 我们需要什么功能。
      2. 首先需要把每个人的分数上传到服务端,然后每个客户端可以从服务端获取前十
        名的数据。
      3. 分数已经有了,应该如何上传到服务器上呢?
      4. 查看相关文档 关于保存对象的知识
      5. 设置一个功能,当游戏结束后弹出一个文本框,让玩家输入一个自定义的名字,
        并通过按“上传分数”的按钮进行保存分数和对应的玩家昵称,也就是上传到服务器上。
      6. 创建UI右键panel
      7. 设置游戏结束后出现的这个面板,转换到2D模式有利于编辑。1.
      8. 在这个panel下创建一个InputField用来让用户填写自己的名字。
      9. 然后同样的方法创建一个按钮,这个按钮的功能是为了提交分数,
        并且有利于下一步的重新开始游戏。
      10. 适当调整它们的位置,并找到其下的text改变font size 。
      11. 在placeholder里设置一些参数,比如提示字符的大小,改变为“请输入昵称”
      12. 同理button里也需要改变text的大小和内容“上传分数”
    • 在代码里实现
      游戏结束后弹出这个panel,玩家填写昵称,并将分数和昵称上传到服务器上。上传完分数后出现排行榜的列表。
      一步一步来,首先定义

      public GameObject SaveScorePanel; public InputField NameField;public Button SaveButton;
      

      相当于初始征求空间后才能进行引擎。
      然后在游戏结束时让面板显示

      SaveScorePanel.SetActive(true);”
      

      然后需要为button绑定一个事件,在这里用代码的方法进行实现。
      在start 方法里

      SaveButton.onClick.AddListener(OnClickSaveButton);
      

      调取savebutton的链接功能,链接的是一个方法

      void OnClickSaveButton(){ };
      

      在这个方法里首先获取用户填写的昵称并给这个昵称定义一个变量

      var nickname = NameField.text;
      

      之后需要到leancloud里对象保存的方法,里面的代码可以拿过来使用,记得引入头文件

      using LeanCloud;
      

      保存对象的代码如下

      AVObject gameScore = new AVObject("GameScore");
        gameScore["score"] = _score;
        gameScore["playerName"] = "nickname";
        gameScore.SaveAsync()”
      
    • 显示分数排名

      1. 同前面的上传分数一样,在UI里需要建一个panel,

      2. 为了与前一个有所区分,重命名一下“RankPanel”

      3. 在它的基础上建一个“Scroll View”并在content下新建text,

      4. 在text里设置一个昵称和分数,用于模拟编辑,

      5. 给content添加一个组件“vertical layout group”

      6. 还需要一个重新开始的“button”用于重新进行游戏。

      7. 开始在代码里实现排名和重新开始的功能。
        同理先定义变量

        public GameObject RankPanel;public GameObject RankItem;public Button RestartButton;
        

        同理给这个Button进行一个时间连接

         RestartButton.onClick.AddListener(()=>
        {
           SceneManager.LoadScene(0);
         });”
        
    • 实现排名

      1. 如何实现排名呢?定义一个函数

        void ShowRankPanel(){ };
        
      2. 要实现排名的功能,需要先获取数据,用到官方文档里的 查询模块的讲解,复制查询的相关代码

      AVQuery<AVObject> query = new AVQuery<AVObject>("GameScore").OrderByDescending("score").Limit(10);
      
      1. 这里是用到查询的功能,对查找到的数据进行降序排列,然后对数据有一个10个的限制。
        接着需要获得这个数据
      query.FindAsync()
      
      1. 因为对这些数据是在子线程进行操作的,为了加快进程,我们要下载一个UNIRX的 插件,用其中的功能去回调结果,从而得到查找的结果。(在查询结束后就调用)

      2. 对整个子线程设为t,在这个子线程t里进行操作,获取t里的结果(Result)用一个变量results装起来.再定义一个字符串的列表用scores装起来。

      3. 在foreach里进行循环

        foreach(var result in results){ }”
        

        foreach是循环遍历results集合里面的元素直到遍历完results中的所有元素,遍历
        results时,每次遍历都将results集合中的元素作为var类型赋给result)。

      4. 前面已经定义了一个字符串的列表了,我们需要对列表进行一个填充,

      var score = result["playerName"] + ":" + result["score"];
      
      1. 让遍历后的结果(result)以“昵称:分数”的形式(score)。把这个形式放入前面定义过的字符串列表scores里。
    • 转换到主线程

      1. 使用

         MainThreadDispatcher.Send();
        

        传送插件,在这个括号里用匿名函数进行遍历,将子线程的数据返回到主线程,
        这样才能把游戏场景里的东西和代码串起来。

      2. => { }”(匿名函数,下划线代表这个变量不会被引用)在这个函数下用

        foreach(var sco in scores){ }
        
      3. 将scores里遍历的结果用sco装起来,为了让游戏场景里的ui物体显示出来,“{}”里面先写下

        var item = Instantiate(RankItem);
        
      4. 将生成的text(RankItem)用变量item装起来

        item.GetComponent<Text>().text = sco;
        
      5. 获取到变量item的text组件设置为sco;send现在不需要设为null;把分数排行的panel状态打开

         RankPanel.SetActive(true);
        
    • 串联起来

      1. 把相对应的ui拖进空槽,并在inspector 面板里关闭。

      2. 把它们串起来,在点击上传的方法( void OnClickSaveButton(){})里,
        在保存数据的同时执行上面写下的的

         ShowRankPanel();
        

        的函数。这里同样用匿名函数的方式

         gameScore.SaveAsync().ContinueWith(_=>{ });
        

        组后将这个插件在start方法里初始化一下

         MainThreadDispatcher.Initialize();
        
    • 完善

      foreach(var sco in scores){ }
      

      里的内容,为了使item和rankItem平级,用setParent 把item的parent 设置为rankItem的parent.
      再回到游戏场景里,将content下的text禁用掉,与此同时,代码里需要将它打开

      item.SetActive(true);
      

    6.随机生成不同形状的台子

    • 预制体
      在场景里的assets里建立prefab文件夹,并将新建的两个形状作为预制体,相当于磨具放到预制体的文件夹里

    • 代码实现
      为了得到一个盒子仓库,可以放上各种盒子的prefab,用于动态生成。定义一个数组。

      public GameObject[] BoxTemplates;
      

      void SpawnStage(){};
      

      的方法里进行生成,命名一个游戏对象

      GameObject prefab;
      

      然后进行随机生成数,从仓库里取出盒子进行生成

        if (BoxTemplates.Length > 0)
        {
            //从盒子库中随机取盒子进行动态生成
            prefab = BoxTemplates[Random.Range(0, BoxTemplates.Length)];
        }
        else
        {
            prefab = Stage;
        }”
      

      将生成的语句里原来的stage 改为

      var stage = Instantiate(prefab);
      

    7.小人翻转跳跃

    • 插件使用
      现在用到UniRX这个插件里的小人翻转跳跃的实现,为什么要用这个插件,因为以上都是在子线程,没有对象参与,所以需要把子线程转化到主线程。应用一下命名空间“using UniRx;”,这样才不会报红。

    • 代码实现
      这里用到doMoveRotate实现小人的旋转

      transform.DOLocalRotate(new Vector3(0, 0, -360), 0.6f, RotateMode.LocalAxisAdd);
      

      前面已经定义了

       public GameObject RankPanel; public GameObject RankItem;”现在可以开始使用插件了。
      

    8.添加音频

    • 基础操作
      新建一个空物体,添加audio source 组件,导入音频文件
    • 代码
      对音频进行调用
    • 建立链接
      拖拽代码和音频

      四.项目学习成果

    本次项目学习所获得的知识点有如下:

    1. 编辑器的基本用法及操作
    2. c sharp 编程的基础知识
    3. 刚体rigid body组件的相关使用方法
    4. Do-Tween插件的动画功能使用
    5. 蓄力粒子系统
    6. 简单的UGUI
    7. 联网排行榜的功能实现
    8. 预制体的使用

    五.提升及展望

    1. UI界面不够美观 界面不统一
    2. 关卡的设置不够完善
    3. 音频只添加了背景音乐 不够生动
    4. 游戏的场景和角色不够生动

    相关文章

      网友评论

          本文标题:案例学习项目报告书

          本文链接:https://www.haomeiwen.com/subject/muwwvftx.html