[虚拟现实] 手把手,一起开发一个基于VR的投篮球小游戏 您所在的位置:网站首页 篮球maya [虚拟现实] 手把手,一起开发一个基于VR的投篮球小游戏

[虚拟现实] 手把手,一起开发一个基于VR的投篮球小游戏

2023-06-21 19:40| 来源: 网络整理| 查看: 265

背景

今天打开落灰的VR头盔时,突然发现两年前开发的VR小游戏,故写此篇博客与大家分享当时开发的过程。本博文以交互为主,不讨论3D建模的相关知识。

项目介绍

VR投篮球玩法: 玩家通过按下控制器的Trigger(即扳机)键来拾取篮球,把篮球投入篮框里得分,距离越远分数越高,一局有10次投篮机会。

项目依赖

运行设备: HTC VIVE / HTC VIVE PRO

操作系统: 支持Mac OS 和 Windows

运行环境: SteamVR

开发环境:

引擎:Unity 2020

IDE:Visual Studio 2019

依赖的工具包:VRTK 2.0

完整工程代码: GitHub

项目实施

开始项目之前 我们需要准备:

1.篮球3D模型及贴图

2.篮球场的3D模型

3.SteamVR sdk

4.VRTK包

开发环境的搭建可以自行百度搜索。

图1

图1

思路/开发过程

为了使思路清晰简洁,我把需要实现的功能和需要用到的技术以一一对应的方式呈现。

1.捡球投球 --> 基于控制器的抓取,释放

2.场地的不同方向移动–>基于控制器的传送机制 (注:防止晕动症的发生这里使用瞬移的方式,游戏中玩家的匀速或非匀速运动都会引起不适)

3.进球计分 --> 碰撞体判断

4.球出界自动返回原点 --> 碰撞体判断

5.计算剩余球数 --> 物体三维坐标跟踪

6.游戏结束的UI交互–>Unity UI交互

篮球

首先从篮球的特性入手,篮球具有弹性,符合物理规律,所以需要给篮球Rigidbody和Collider组件,让其受地球重力影响,并与地面发生碰撞而不是穿过地面。此时的篮球还未具有弹性,需要我们手动定义。

在project面板中新建一个Physics Material材质,并设置弹力大小以及动摩擦和静摩擦力大小。并将材质赋给篮球模型。此时篮球就可以自由下落和弹起。参数设置如图所示。

图3

篮球的属性设置完成后我们可以调整篮球在场景中的位置,以便玩家进入游戏时拾取。

篮框

当玩家把篮球投进篮框时,篮框需要作出相应的处理:计算得分,并计算剩余球数。

要使篮框作出处理首先需要为篮框添加触发器,当球投入篮框时进行相应处理,否则无动作。具体流程如下:

首先,在篮框内部新建一个空的GameObject,调整GameObject位置及尺寸,使得篮球经过篮框时会与此游戏对象发生碰撞,实现加分处理。

此GameObject无需符合物理规律,不受外力影响。不可被玩家看见,并且篮球与其发生碰撞时可以直接穿过。所以我们为其添加一个BoxCollider,编辑好Collider,使其覆盖物体外围,并在Collider属性中勾选Is Trigger,使其成为一个触发器。

最后,新建一个tag,命名为trigger,并赋给此GameObject。后面写后台交互代码时会用到。

这时,我们的篮框触发器就设置好了。

UI界面

本项目目的在于总结VR交互技术,所以把UI的权重降低了,但是UI在实际开发中是很重要的。

我们只做一个游戏结束的UI界面和游戏信息显示界面。

首先在Unity的Hierarchy面板中新建一个GameObject 命名为GameOverUI,再把我们的篮球模型复制一个进去,作为UI的子物体,接着创建3D Text 作为子物体,内容为:Game Over。调整UI位置以及大小,注意放在显眼的位置。

然后将做好的GameOverUI拖入Project面板下的Prefab文件夹中,使其成为预制体。然后删除Hierarchy面板中的GameOverUI。这样我们的游戏结束界面就做好了。

同理,我们来制作游戏信息显示界面

在Hierarchy面板右键,选择UI-Canvas新建一个Canvas,重命名为GameInfo

然后在Canvas中新建4个Text,分别用于显示分数文字,分数,剩余球数文字,剩余球数。如图

图4

VRTK控制器

导入了VRTK包后需要对控制器进行设置。

我们定义左手控制器用于传送,右手控制器用于投篮。

所以我们为左手控制器添加如下几个组件:

左手

为右手控制器添加:

右手

属性参数全部默认即可,需要定制可自行查阅文档定制。这里也不多赘述。

后台交互

到此,我们已经准备就绪,可以开始敲后台的交互代码。

完整源码请前往GitHub下载。

我第一个实现的功能是球出界自动回到玩家面前。经过考虑为了可玩性就没有设置自动抓取。

代码分析: 要实现这个功能只需要在Update函数中不停判断球和玩家摄影机的位置即可。同时有个问题需要注意。如果单把球移到玩家面前我们会发现球还在不停弹跳。由于球具有一定的初速度,且符合物理规律,所以这个现象是正常的。我们要解决它,就将球移动时的初速度设为0,即所谓的“瞬移”。

代码片段如下:

if(transform.position.y Debug.Log("OnTriggerEnter触发!Tag是" + other.tag); if (other.CompareTag("trigger") && !isGameOver) { //Debug.Log("球进了!"); //距离计算 Debug.Log(distanceCal(other)); //总分统计 总分=基础分+距离四舍五入分 scoreCount += basicScore + (int)Math.Round(distanceCal(other), 0); //刷新显示 score.text = scoreCount.ToString(); } if (isGameOver) { RestartGame(); } }

这里我们的分数不是简单的+1操作。

进球的得分=基础分+距离分

所以我们需要判断玩家投球的位置离篮框有多远。思路是计算两个空间向量之间的距离。我们可以使用距离公式来实现,也可以更简单地调用Vector3类中的distance()函数来计算出距离,效率上差别不大。

我自己封装了一个距离计算函数,代码如下:

//计算出玩家与碰撞体之间的距离,用于统计分数 private float distanceCal(Collider other) { Vector3 v_player = Camera.main.transform.position; Vector3 v_collider = other.transform.position; float distance = Vector3.Distance(v_player, v_collider); return distance; }

这样,在进球时的得分就会根据距离改变,距离越远,分数越高。

接着我们需要来判断什么时候从剩余球数里面减一。

我最初的思路是判断控制器扳机放开的瞬间,把球数减一。但考虑到玩家可能手滑或者需要运球,这样就会导致剩余球数频繁减少,直接Game over。

所以我决定换一个思路。当球达到一定高度时剩余球数才会减少。这样可以防止手滑或者运球造成的“冤枉”。

这个地方有一个注意点:球本身是有弹性的,如果不加判断的话,落地后重新弹起也会造成剩余球数减少,球高抛在空中停留也会造成次数多次减少。所以我们只将第一次达到那个高度时的球称为有效,其他均无效。这就需要引入一个开关变量,作为标志。

定义一个bool变量,命名为ballCircle

当达到一定高度时,ballCircle为真,直到球贴地并且末速度接近0时ballCircle才设为假,重新开始下一球的判断。

此部分代码如下:

rb = GetComponent(); if(transform.position.y >= 2.5f && !ballCircle) { totalBall--; ball.text = totalBall.ToString(); ballCircle = true; } //Debug.Log("速度: x: " + rb.velocity.x + " y: " + rb.velocity.y + " z: " + rb.velocity.z); if (rb.velocity.y > -1.0f && rb.velocity.y isGameOver = true; Debug.Log("游戏结束"); //生成游戏结束UI restartUI = Instantiate(GameOverUI); rb = restartUI.GetComponent(); rb.angularVelocity = new Vector3(0, 2, 0);//让UI旋转,提升体验 restartUI.transform.position = new Vector3(-4, 4, 3); }

玩家要重新开始游戏怎么办?

有很多种方法,我想到用控制器去触碰游戏结束界面来重新开始游戏,但后来想想,觉得很麻烦,玩家需要先传送到UI前面才能触碰到。后来就采用简单粗暴的方法,当玩家重新拾取篮球时直接开始新的一局,重置分数和剩余球数。即简单又方便。代码如下:

if (isGameOver) { RestartGame(); } //重新开始游戏 public void RestartGame() { Debug.Log("游戏将在1秒后重新开始..."); Destroy(restartUI); //延时调用初始化函数 Invoke("init", 1); }

思路其实很简单,同样是用一个bool变量来存储游戏是否结束的状态,然后进行相关处理。

总结

本项目涵盖了VR交互的基础知识点,包括unity3d的基本操作、SteamVR以及VRTK sdk的使用、VR控制器的交互:抓取物体、传送、控制器事件、C Sharp编程等内容。

完整的工程文件可在GitHub上下载,以及成品都会发布在GitHub。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有