【Unity3D开发小游戏】Unity3D零基础一步一步教你制作跑酷类游戏

简介: 【Unity3D开发小游戏】Unity3D零基础一步一步教你制作跑酷类游戏

一、前言

最近跑酷游戏比较流行,开发教程也很多,但是大部分都是不太详细,这篇文章就带着大家一步一步开发出来一个跑酷类的游戏,教程比较基础,适合大部分Unity开发的初学者。 还有就是,此专栏已经开通收费,里面整合的都是小游戏的开发教程,想要学习Unity开发游戏的,都可以订阅一下。 如果文章出现什么问题,就及时联系我

二、效果图&下载链接

1702378762774.jpg

三、教程

在教程开始之前,我们分析一下跑酷类游戏制作思路。 首先是道路和障碍物,我们可以先设置三段道路,然后障碍物随机生成 道路中间有抵达点,角色到达抵达点判断是否将后面的道路移动到前面接起来。 首先到达第一段的抵达点,肯定是不切换 到达第二段的抵达点,将1号路段移动到最前面 到达第三段的抵达点,将2号路段移动到最前面 循环往复,无穷尽也 然后是主角的移动脚本,躲避障碍物,移动位置固定三个点,可以跳,可以铲地 主角碰到障碍物就挂,游戏结束

1、新建项目

博主的Unity版本是Unity5.6.1f1,推荐大家使用我这个版本,或者其他的5.6.x版本,不然可能会出现各种各样奇奇怪怪的问题。

1702378786457.jpg

文件目录的话就按照我这个目录来,比较清晰明了。

2、导入资源

资源已经上传到gitee,需要的可以下载

1702378800715.jpg

1702378810058.jpg

1702378815476.jpg

3、处理动画资源

1702378825652.jpg

可以看到所有的动画文件都有。 接着我们就可以新建一个Animator Controller文件来管理动画文件。

1702378840169.jpg

命名随意。

1702378853722.jpg

接着我们将动画剪辑拖到Animator处理面板中:

1702378877265.jpg

默认状态是run,然后有jump 、slide、idle

1702378886894.jpg

接着就是“Take Transition”将run和jump 以及 run 、slide、idle连下线。

设置两个bool值,来控制动画的切换:

1702378912208.jpg

1702378919597.jpg

1702378924530.jpg

接下来我们就可以在场景中看一下动画效果了:

1702378938727.jpg

4、处理路段模型

1702378949170.jpg

首先我们找到导入的资源SimpleRoadwork,里面有一个Demo场景,点进去可以看一下各类模型:

1702378964633.jpg

在Prefabs文件夹中,可以找到我们需要的各类模型,包括路面、路标、障碍物:

1702378974356.jpg

接下来,我们就设计一下路面:

1702378983868.jpg

1702378990670.jpg

接着摆放路标:

1702379000400.jpg

接着摆放障碍物:

1702379013461.jpg

因为障碍物我们要后期自动生成,现在就可以先隐藏起来。

1702379048397.jpg

然后设置到达点(到达点的目的是当角色到达这个位置的时候,自动切换路线):

1702379074367.jpg

隐藏它的Mesh Renderer ,将BoxCollider IsTrigger设置成true:

1702379110313.jpg

路段就完成了:

1702379124024.jpg

整个目录如下:

1702379134858.jpg

不会摆放也没有关系,我已经设置好了,用我的也行。

5、主角模型处理

1702379149425.jpg

明显是有点大,我们给它同比例缩小一下:

1702379157620.jpg

1702379163631.jpg

接着设置一下摄像机的视角:

1702379172126.jpg

1702379179650.jpg

6、生成障碍物

新建脚本Control_Scenes.cs 我们首先来生成障碍物:

using UnityEngine;
public class Control_Scenes : MonoBehaviour
{
    public GameObject[] m_ObstacleArray;
    public Transform[] m_ObstaclePosArray;
    void Start()
    {
      //因为要生成3段路段,所以生成的障碍物也是不同的,传递不同的下标生成不同的路段障碍物
        Spawn_Obstacle(1);
    }
  //生成障碍物
  public void Spawn_Obstacle(int index)
    {
        //销毁原来的对象
        GameObject[] obsPast = GameObject.FindGameObjectsWithTag("Obstacle" + index);
        for (int i = 0; i < obsPast.Length; i++)
        {
            Destroy(obsPast[i]);
        }
        //生成障碍物
        foreach (Transform item in m_ObstaclePosArray[index])
        {
            GameObject prefab = m_ObstacleArray[Random.Range(0, m_ObstacleArray.Length)];
            Vector3 eulerAngle = new Vector3(0, Random.Range(0, 360), 0);
            GameObject obj = Instantiate(prefab, item.position, Quaternion.Euler(eulerAngle));
            obj.tag = "Obstacle" + index;
        }
    }
}

将脚本挂在Control_Scenes对象上: 将Prefab文件夹里面的模型拖入到ObstacleArray对象数组卡槽中 将隐藏以后的障碍物拖入到ObstaclePosArray对象数组卡槽中

1702379196069.jpg

添加Tag:

1702379206184.jpg

运行看一下:

1702379215652.jpg

7、路段切换

我们接着打开Control_Scenes.cs 添加函数:

bool m_ISFirst;
public GameObject[] m_RoadArray;
void Start()
{
        //Spawn_Obstacle(1);
        Change_Road(1);
}
public void Change_Road(int index)
{
        if (m_ISFirst && index == 0)
        {
            m_ISFirst = false;
            return;
        }
        else
        {
            int lastIndex = index - 1;
            if (lastIndex < 0)
                lastIndex = 2;
            m_RoadArray[lastIndex].transform.position = m_RoadArray[lastIndex].transform.position - new Vector3(150, 0, 0);
            Spawn_Obstacle(lastIndex);
        }
}

PS:这里解释一下代码,怎么切换的呢 举个例子,角色跑到了第二段,那么第一段要移动到第三段后面隔一个路段长度的距离,接下来画个图:

1702379239498.jpg

那么为啥x轴减去150。这是因为我发现这三条路段的距离都差了50,坐标轴是负轴,所以就减去了150。

1702379249760.jpg

我们可以测试一下效果:

1702379259638.jpg

但是仅仅这样是不够的,我们还需要在角色到达抵达点的时候,切换路线,当然第一段路不用切换,因为再切就没了。。 这个在我们写完角色移动以后再补充。

8、角色移动

新建脚本:Control_Player.cs 说明一下:因为我们设定的三条道,所以角色只能在三条道里面切换。那么只需要改变角色的z值就可以了。如果角色在最左边,那么只能往右移动,同理在最右边,只能往左移动,在中间两边都可以移动。

接着我们就可以看一下z轴的值: 中间:

1702379273395.jpg

1702379279990.jpg


左边:

1702379289536.jpg

1702379295211.jpg

1702379305547.jpg

1702379316735.jpg

代码:

using UnityEngine;
public class Control_Player : MonoBehaviour
{
    //前进速度
    public float m_ForwardSpeeed = 7.0f;
    //动画组件
    private Animator m_Anim;
    //动画现在状态
    private AnimatorStateInfo m_CurrentBaseState;
    //动画状态参照
    static int m_jumpState = Animator.StringToHash("Base Layer.jump");
    static int m_slideState = Animator.StringToHash("Base Layer.slide");
    // Use this for initialization
    void Start()
    {
        m_Anim = GetComponent<Animator>();
    }
    // Update is called once per frame
    void Update()
    {
        transform.position += Vector3.left * m_ForwardSpeeed * Time.deltaTime;
        m_CurrentBaseState = m_Anim.GetCurrentAnimatorStateInfo(0);
        if (Input.GetKeyDown(KeyCode.W))
        {
            m_Anim.SetBool("jump", true);
        }
        else if (Input.GetKeyDown(KeyCode.S))
        {
            m_Anim.SetBool("slide", true);
        }
        else if (Input.GetKeyDown(KeyCode.A))
        {
            Change_PlayerZ(true);
        }
        else if (Input.GetKeyDown(KeyCode.D))
        {
            Change_PlayerZ(false);
        }
        if (m_CurrentBaseState.fullPathHash == m_jumpState)
        {
            m_Anim.SetBool("jump", false);
        }
        else if (m_CurrentBaseState.fullPathHash == m_slideState)
        {
            m_Anim.SetBool("slide", false);
        }
    }
    public void Change_PlayerZ(bool IsAD)
    {
        if (IsAD)
        {
            if (transform.position.z == -14.7f)
                return;
            else if (transform.position.z == -9.69f)
            {
                transform.position = new Vector3(transform.position.x, transform.position.y, -14.7f);
            }
            else
            {
                transform.position = new Vector3(transform.position.x, transform.position.y, -9.69f);
            }
        }
        else
        {
            if (transform.position.z == -6.2f)
                return;
            else if (transform.position.z == -9.69f)
            {
                transform.position = new Vector3(transform.position.x, transform.position.y, -6.2f);
            }
            else
            {
                transform.position = new Vector3(transform.position.x, transform.position.y, -9.69f);
            }
        }
    }
}

键盘WSAD控制上跳,下滑,左右移动等操作。现在就可以去试试啦。

1702379338150.jpg

1702379344525.jpg

但是,有一点哈,角色怎么越跑越远离开了我们呢,因为,还没有写摄像机跟随脚本,接下来继续吧。

9、摄像机跟随

新建脚本Control_Camera.cs

using UnityEngine;
public class Control_Camera : MonoBehaviour
{
  //间隔距离
  public float m_DistanceAway = 5f;
  //间隔高度
  public float m_DistanceHeight = 10f;
  //平滑值
  public float smooth = 2f;               
  //目标点
  private Vector3 m_TargetPosition;      
  //参照点
  Transform m_Follow;        
  void Start()
  {
    m_Follow = GameObject.Find("Player").transform;
  }
  void LateUpdate()
  {
    m_TargetPosition = m_Follow.position + Vector3.up * m_DistanceHeight - m_Follow.forward * m_DistanceAway;
    transform.position = Vector3.Lerp(transform.position, m_TargetPosition, Time.deltaTime * smooth);
  }
}

OK 摄像机跟着Player跑起来了

10、碰撞检测

我们需要不停的躲避障碍物,一旦碰撞到障碍物就dead了 我们首先修改障碍物的碰撞器属性:

1702379367408.jpg

Mesh Collider : Is Trigger=true

1702379379846.jpg

我们给Player加上刚体和碰撞体:

1702379392017.jpg

注意要勾上Is Trigger

打开Control_Player.cs脚本:

//游戏结束
bool m_IsEnd = false;
void OnGUI()
{
        if (m_IsEnd)
        {
            GUIStyle style = new GUIStyle();
            style.alignment = TextAnchor.MiddleCenter;
            style.fontSize = 40;
            style.normal.textColor = Color.red;
            GUI.Label(new Rect(Screen.width / 2 - 100, Screen.height / 2 - 50, 200, 100), "你输了~", style);
        }
}
void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.name == "Vehicle_DumpTruck" || other.gameObject.name == "Vehicle_MixerTruck")
        {
            m_IsEnd = true;
            m_ForwardSpeeed = 0;
            m_Anim.SetBool("idle", true);
        }
    }

11、切换道路

在第七章的时候就已经写好了道路切换,但是一直没有讲到碰撞检测,那么现在我们就结合碰撞检测,检测到抵达点然后切换道路吧

我们首先找到三个抵达点:MonitorPos 然后改名叫做MonitorPos0 MonitorPos1 MonitorPos2

1702379413934.jpg


修改一下BoxCollider的Is Trigger属性:

1702379422465.jpg

修改Control_Player.cs的代码:

 //场景控制对象
Control_Scenes m_ControlScenes;
void Start()
{
        m_Anim = GetComponent<Animator>();
        m_ControlScenes = GameObject.Find("Control_Scenes").GetComponent<Control_Scenes>();
}
void OnTriggerEnter(Collider other)
{
        if (other.gameObject.name == "Vehicle_DumpTruck" || other.gameObject.name == "Vehicle_MixerTruck")
        {
            m_IsEnd = true;
            m_ForwardSpeeed = 0;
            m_Anim.SetBool("idle", true);
        }
        if (other.gameObject.name == "MonitorPos0")
        {
            m_ControlScenes.Change_Road(0);
        }
        else if (other.gameObject.name == "MonitorPos1")
        {
            m_ControlScenes.Change_Road(1);
        }
        else if (other.gameObject.name == "MonitorPos2")
        {
            m_ControlScenes.Change_Road(2);
        }
}


相关文章
|
3月前
|
图形学 C#
超实用!深度解析Unity引擎,手把手教你从零开始构建精美的2D平面冒险游戏,涵盖资源导入、角色控制与动画、碰撞检测等核心技巧,打造沉浸式游戏体验完全指南
【8月更文挑战第31天】本文是 Unity 2D 游戏开发的全面指南,手把手教你从零开始构建精美的平面冒险游戏。首先,通过 Unity Hub 创建 2D 项目并导入游戏资源。接着,编写 `PlayerController` 脚本来实现角色移动,并添加动画以增强视觉效果。最后,通过 Collider 2D 组件实现碰撞检测等游戏机制。每一步均展示 Unity 在 2D 游戏开发中的强大功能。
172 6
|
2月前
|
测试技术 C# 图形学
掌握Unity调试与测试的终极指南:从内置调试工具到自动化测试框架,全方位保障游戏品质不踩坑,打造流畅游戏体验的必备技能大揭秘!
【9月更文挑战第1天】在开发游戏时,Unity 引擎让创意变为现实。但软件开发中难免遇到 Bug,若不解决,将严重影响用户体验。调试与测试成为确保游戏质量的最后一道防线。本文介绍如何利用 Unity 的调试工具高效排查问题,并通过 Profiler 分析性能瓶颈。此外,Unity Test Framework 支持自动化测试,提高开发效率。结合单元测试与集成测试,确保游戏逻辑正确无误。对于在线游戏,还需进行压力测试以验证服务器稳定性。总之,调试与测试贯穿游戏开发全流程,确保最终作品既好玩又稳定。
98 4
|
3月前
|
图形学 缓存 算法
掌握这五大绝招,让您的Unity游戏瞬间加载完毕,从此告别漫长等待,大幅提升玩家首次体验的满意度与留存率!
【8月更文挑战第31天】游戏的加载时间是影响玩家初次体验的关键因素,特别是在移动设备上。本文介绍了几种常见的Unity游戏加载优化方法,包括资源的预加载与异步加载、使用AssetBundles管理动态资源、纹理和模型优化、合理利用缓存系统以及脚本优化。通过具体示例代码展示了如何实现异步加载场景,并提出了针对不同资源的优化策略。综合运用这些技术可以显著缩短加载时间,提升玩家满意度。
136 5
|
2月前
|
前端开发 图形学 开发者
【独家揭秘】那些让你的游戏瞬间鲜活起来的Unity UI动画技巧:从零开始打造动态按钮,提升玩家交互体验的绝招大公开!
【9月更文挑战第1天】在游戏开发领域,Unity 是最受欢迎的游戏引擎之一,其强大的跨平台发布能力和丰富的功能集让开发者能够迅速打造出高质量的游戏。优秀的 UI 设计对于游戏至关重要,尤其是在手游市场,出色的 UI 能给玩家留下深刻的第一印象。Unity 的 UGUI 系统提供了一整套解决方案,包括 Canvas、Image 和 Button 等组件,支持添加各种动画效果。
132 3
|
2月前
|
设计模式 存储 人工智能
深度解析Unity游戏开发:从零构建可扩展与可维护的游戏架构,让你的游戏项目在模块化设计、脚本对象运用及状态模式处理中焕发新生,实现高效迭代与团队协作的完美平衡之路
【9月更文挑战第1天】游戏开发中的架构设计是项目成功的关键。良好的架构能提升开发效率并确保项目的长期可维护性和可扩展性。在使用Unity引擎时,合理的架构尤为重要。本文探讨了如何在Unity中实现可扩展且易维护的游戏架构,包括模块化设计、使用脚本对象管理数据、应用设计模式(如状态模式)及采用MVC/MVVM架构模式。通过这些方法,可以显著提高开发效率和游戏质量。例如,模块化设计将游戏拆分为独立模块。
180 3
|
3月前
|
图形学 开发者 存储
超越基础教程:深度拆解Unity地形编辑器的每一个隐藏角落,让你的游戏世界既浩瀚无垠又细节满满——从新手到高手的全面技巧升级秘籍
【8月更文挑战第31天】Unity地形编辑器是游戏开发中的重要工具,可快速创建复杂多变的游戏环境。本文通过比较不同地形编辑技术,详细介绍如何利用其功能构建广阔且精细的游戏世界,并提供具体示例代码,展示从基础地形绘制到植被与纹理添加的全过程。通过学习这些技巧,开发者能显著提升游戏画面质量和玩家体验。
147 3
|
2月前
|
图形学 C++ C#
Unity插件开发全攻略:从零起步教你用C++扩展游戏功能,解锁Unity新玩法的详细步骤与实战技巧大公开
【8月更文挑战第31天】Unity 是一款功能强大的游戏开发引擎,支持多平台发布并拥有丰富的插件生态系统。本文介绍 Unity 插件开发基础,帮助读者从零开始编写自定义插件以扩展其功能。插件通常用 C++ 编写,通过 Mono C# 运行时调用,需在不同平台上编译。文中详细讲解了开发环境搭建、简单插件编写及在 Unity 中调用的方法,包括创建 C# 封装脚本和处理跨平台问题,助力开发者提升游戏开发效率。
200 0
|
2月前
|
vr&ar 图形学 API
Unity与VR控制器交互全解:从基础配置到力反馈应用,多角度提升虚拟现实游戏的真实感与沉浸体验大揭秘
【8月更文挑战第31天】虚拟现实(VR)技术迅猛发展,Unity作为主流游戏开发引擎,支持多种VR硬件并提供丰富的API,尤其在VR控制器交互设计上具备高度灵活性。本文详细介绍了如何在Unity中配置VR支持、设置控制器、实现按钮交互及力反馈,结合碰撞检测和物理引擎提升真实感,助力开发者创造沉浸式体验。
147 0
|
2月前
|
图形学 开发者
【独家揭秘】Unity游戏开发秘籍:从基础到进阶,掌握材质与纹理的艺术,打造超现实游戏视效的全过程剖析——案例教你如何让每一面墙都会“说话”
【8月更文挑战第31天】Unity 是全球领先的跨平台游戏开发引擎,以其高效性能和丰富的工具集著称,尤其在提升游戏视觉效果方面表现突出。本文通过具体案例分析,介绍如何利用 Unity 中的材质与纹理技术打造逼真且具艺术感的游戏世界。材质定义物体表面属性,如颜色、光滑度等;纹理则用于模拟真实细节。结合使用两者可显著增强场景真实感。以 FPS 游戏为例,通过调整材质参数和编写脚本动态改变属性,可实现自然视觉效果。此外,Unity 还提供了多种高级技术和优化方法供开发者探索。
52 0
|
2月前
|
图形学 iOS开发 Android开发
从Unity开发到移动平台制胜攻略:全面解析iOS与Android应用发布流程,助你轻松掌握跨平台发布技巧,打造爆款手游不是梦——性能优化、广告集成与内购设置全包含
【8月更文挑战第31天】本书详细介绍了如何在Unity中设置项目以适应移动设备,涵盖性能优化、集成广告及内购功能等关键步骤。通过具体示例和代码片段,指导读者完成iOS和Android应用的打包与发布,确保应用顺利上线并获得成功。无论是性能调整还是平台特定的操作,本书均提供了全面的解决方案。
150 0