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

案例学习项目报告书

作者: 伊才妮 | 来源:发表于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. 游戏的场景和角色不够生动

相关文章

  • 案例学习项目报告书

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

  • 项目报告书的目的

    项目报告书的目的 代写公司:http://www.daixieyun.com/ 项目报告书是申请者在项目管理领域,...

  • HandToHand真实案例(06~10)

    06真实产品案例-案例学习重点与项目背景 项目背景: 1.项目起源 2.项目成员与分工 3.项目目的 07.真实产...

  • 分析案例:贷款逾期分析

    ​通过具体的项目案例,学习面对数据,如何去分析的思路。 下面内容来自社群会员的项目作业。带着下面问题去学习这个案例...

  • 实践|项目化学习案例

    寒假期间,结合孩子的假期作业,和孩子共同设计了一份关于牛意象的项目化学习并指导孩子撰写了一份成果报告,研究项目化学...

  • 项目式学习培训有感2

    学校的项目式学习书目编写仍然在推进,今天项目式学习专家以一个和数学有关的项目式学习案例《设计一个节电方案》来给大家...

  • 分析案例:贷款逾期分析

    ​ 通过具体的项目案例,学习面对数据,如何去分析的思路。 下面内容来自社群会员的项目作业。带着下面问题去学习这个案...

  • flutter版本的玩Android客户端

    flutter学习案例 目录介绍 00.项目下载与查看 01.项目介绍 02.项目优势 03.部分功能介绍 04....

  • flutter版本的玩Android客户端

    flutter学习案例 目录介绍 00.项目下载与查看 01.项目介绍 02.项目优势 03.部分功能介绍 04....

  • 房地产行业前期流程

    房地产行业前期流程 一、立项审批 1、项目立项申请报告书(原件一份) 2、项目建议书或项目可行性研究报告(一份) ...

网友评论

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

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