如何使用Arduino和Unity制作自定义游戏控制器

您曾经想设计自己的游戏控制器吗?

在这个简短的项目中,我们将构建一个简单的自定义游戏控制器,以与Unity游戏引擎一起使用。该控制器将由Arduino Uno提供动力,尽管您也可以在该项目中使用众多替代品之一。我们还将创建一个基本的游戏,在该游戏中,您将使用控制器来避免掉落物体并减慢时间。

对于此项目,您将需要

  • Arduino或类似的微控制器
  • 1 x 10k欧姆电阻
  • 1 x瞬时开关
  • 1 x电位计
  • 连接线
  • 面包板
  • Unity游戏引擎
  • Unity Asset Store中的Uniduino插件($ 30)
  • 完成项目代码,以防万一您不想将其编写出来(不要其中不包含Uniduino插件)
  • 其中大多数东西都可以在Arduino入门套件中找到。如果没有入门工具包,请查看我们的指南,为您选择最合适的指南。

    您可以根据需要使控制器变得复杂,尽管在此示例中,我们将设置一个电位计和一个按钮–非常适合控制简单的街机游戏。

    组装控制器

    设置面包板和Arduino,如下图所示。这就是我们将要用作游戏控制器的东西,尽管您也可以使用与DIY midi控制器几乎完全相同的设置!

    准备Arduino

    一旦拥有一切接线,通过USB连接Arduino。在Arduino Software IDE中,进入工具>板工具>端口以选择要使用的微控制器和端口。 Arduino IDE随附了我们所需的草图,您可以在文件>示例>固件> StandardFirmata 下找到它。单击上载,您就可以开始使用它。

    如果您是Arduino的新手,并且脑袋有些许融化,请查看我们的初学者指南,以帮助您与计算机进行良好的交谈。

    设置您的Unity项目

    在Unity中,打开 Window> Asset Store 以从Unity编辑器中访问Unity的Asset Store。在资产商店中搜索Uniduino插件。这个插件将允许您在Unity内部的Arduino引脚之间收发数据。在撰写本文时,该插件的价格为30美元。可以在不购买插件的情况下进行此项目,尽管它相当复杂,并且您可能会发现插件更加方便。

    来自插件创建者的视频向您介绍了整个过程测试一切正常,以及首次安装。请注意,您可能还必须在Windows上重置Unity编辑器。

    我们可以使用同一测试面板来测试我们的控制器。将引脚D2设置为INPUT和Digital。再往下,将引脚A5设置为ANALOG。电位计和按钮现在应该在屏幕上的引脚号旁边显示值。进步!

    现在可以做一些我们可以控制的事情了

    所以我们有一个控制器,但是我们应该控制什么呢?好吧,可能性无穷无尽,但今天,我们将创建一个非常简单的躲避游戏,以测试我们的新控制系统。我们将非常快地完成游戏设置,因此,如果您是Unity引擎的新手,您可能会发现我们的Unity游戏编程初学者指南很有用。

    我们将在其中构建一个非常基本的游戏您的目标是向左和向右躲避球体以避免掉落立方体,这将利用您新创建的自定义控制器。

    创建一个新场景并将Uniduino预制件从 Assets> Uniduino中拖动>预制件进入层次结构,然后将Uniduino预制件拖入层次结构。

    在Unity层次结构中,单击 Create> Sphere ,然后使用检查器中的“变换"选项卡将其移至底部。游戏画面。

    现在该进行编码了。

    现在,向此聚会添加一些代码。在``层次结构''中选择球体后,单击其检查器窗口底部的添加组件>新脚本。将其命名为 sphereMover ,然后从下拉菜单中选择 C Sharp 。点击创建并添加,脚本将被添加到GameObject中。双击它以打开脚本并输入以下代码:

    using UnityEngine;using System.Collections;using Uniduino;public class sphereMover : MonoBehaviour{ //Headers aren't scrictly neccesary, but they make life easier back in the Inspector. [Header("Arduino Variables")] //we need to declare the Arduino as a variable public Arduino arduino; //we need to declare an integer for the pin number of our potentiometer, //making these variables public means we can change them in the editor later //if we change the layout of our arduino public int potPinNumber; //a float variable to hold the potentiometer value (0 - 1023) public float potValue; //we will later remap that potValue to the y position of our capsule and hold it in this variable public float mappedPot; //public int for our button pin public int buttonPinNumber; [Header("Sphere Variables")] //variables to hold the values we noted earlier for the sides of our screen public float leftEdge; public float rightEdge; // Use this for initialization void Start () {//and initialize we shall, starting with the Arduino Variable.  //we are only using one arduino, so we can use Arduino.global to grab it. arduino = Arduino.global; arduino.Setup(ConfigurePins); } void ConfigurePins() { //configure the Arduino pin to be analog for our potentiometer arduino.pinMode(potPinNumber, PinMode.ANALOG); //Tell the Arduino to report any changes in the value of our potentiometer arduino.reportAnalog(5, 1); //configure our Button pin arduino.pinMode(buttonPinNumber, PinMode.INPUT); arduino.reportDigital((byte)(buttonPinNumber / 8), 1); }}

    Take a moment to read through the code comments. So far, we have declared some variables for our Arduino, its pins, and our Sphere. We have also used the
    Start and ConfigurePins methods to initialize our Arduino on run time. Lets save our script, and go back into the Unity editor and see what’s changed.

    我们现在可以在“检查器"窗口中看到我们的公共变量。让我们看看我们在现阶段可以输入什么以帮助我们以后。我们早先知道我们在Arduino上使用的是什么引脚,我们可以输入它们。我们还从实验中更早地知道了我们希望球体能够左右移动的距离,以使其不会从屏幕上掉落。现在输入这些值。

    生命的最初迹象

    是时候在Unity Editor中实际查看来自Arduino的值了。现在,我们可以在sphereMover脚本的Update函数中添加一行代码,然后再次保存该脚本。

    void Update (){ //We assign the value the arduino is reading from our potentionmeter to our potValue variable potValue = arduino.analogRead(potPinNumber);} 

    现在,我们的potValue变量每帧都会更新,我们可以看到它的实际值时间在Unity Inspector中。在我们进行测试之前,现在是时候检查Uniduino插件是否在正确的端口上进行监听。在层次结构中单击Uniduino,然后在检查器中检查它的端口名称。如果为空白,请为您的Arduino输入正确的端口号。在这种情况下,它是COM4,尽管它可能与您有所不同。如果不确定,请检查使用Arduino IDE。

    在层次结构中选择您的球体,然后单击屏幕顶部的“播放"按钮。系统需要几秒钟的初始化时间,此后您应该在移动电位计时开始在检查器中看到Pot Value变量的变化。

    现在我们在讨论!好吧,严格来说,Unity和Arduino在说话,但是谁在数呢?如果您到此为止并且在检查器中看不到值的变化,请检查设置步骤,并确保为Arduino选择了正确的端口。

    让我们移动这个球体

    现在我们已经更新了potValue变量,我们想使用该值来移动我们的球体。当电位计完全移到左侧时,我们希望球体位于屏幕的左侧,反之亦然。 Unity中的对象位于Vector空间中的某个点,该点由其Transform.position的值确定。在下图中,球体位于我们想要的最左侧,您可以看到它的位置向量是9.5,-4,0。

    我们想影响球体的X位置。不幸的是,直接使用电位器的值将不起作用,因为当电位器一直向左移动时,其值为0,这会使我们的球体位于屏幕中间。在另一个极端,电位器的最高值1023将使立方体远离屏幕的右侧。没用。我们需要的是一些数学。

    为什么Unity会为您做数学呢?

    对于那些在那里的人,他们害怕地盯着一张无意义数字覆盖的纸(尽管有一些很棒的网站可以帮助您学习数学),但不要害怕。我们需要一种使电位计值与球体的X位置相对应的方法。幸运的是,我们可以使用扩展方法

    扩展方法是为我们完成特定工作的脚本。在这种情况下,我们给它提供我们拥有的值,并返回它们之间的映射关系,以便在我们的 sphereMover 脚本中使用。在“项目"面板的顶部,单击创建> C#脚本,并将其命名为ExtensionMethods。在脚本中输入以下代码:

    using UnityEngine;using System.Collections;public static class ExtensionMethods {	 //our handy dandy Remapper function	public static float Remap (this float value, float from1, float to1, float from2, float to2) 	{		return (value - from1) / (to1 - from1) * (to2 - from2) + from2;	}}

    保存脚本,然后回到您的sphereMover脚本。现在,我们可以在Update函数的ExtensionMethods脚本中使用此Remap函数,将电位计值转换为游戏中可用的值。在我们刚刚分配potValue变量的位置,键入以下内容:

    提示显示我们的Remap接受了两组From和To值,并将它们映射在一起。我们可以在其中输入值。

    mappedPot = potValue.Remap(0, 1023, leftEdge, rightEdge);

    保存脚本,返回Unity编辑器,然后单击“播放"按钮。现在,您应该看到,移动电位计时,“映射的电位器"变量会发生变化,以与我们为左右边缘确定的值相对应。花一点时间坐下来,感谢您的ExtensionMethods脚本。

    注意:如果您注意到自己的值是相反的,那么当电位计一直向右移动时,您的Mapped Pot变量将得到负值,可能您的电位计设置错误。幸运的是,您无需进行任何重新布线即可解决此问题。您可以在重新映射它们时简单地切换这些值:

    现在,我们终于有了可用的值。现在剩下要做的就是将这些值分配给我们球体的X位置:

    //Assign the mapped pot value to the sphere's x position transform.position = new Vector3(mappedPot, transform.position.y, transform.position.z);

    保存脚本,返回Unity编辑器并按播放。现在,您应该可以使用电位计左右移动Sphere!

    将按钮置于工作状态

    现在我们可以移动球形了,不是吗?当我们在狭窄的地方时,有什么办法可以减慢速度吗?我们将使用按钮来减慢游戏时间。打开您的sphereMover脚本,并将此代码添加到您的Update函数中

    //if Unity detects the button is being pressed, the time scale slows down if (arduino.digitalRead(buttonPinNumber) == 1){ Time.timeScale = 0.4f; } else Time.timeScale = 1.0f;

    现在,我们已经掌握了游戏的机制,请添加一些障碍!我们将使用球体的天敌,即立方体。在层次结构中,单击创建> 3d对象>多维数据集。在多维数据集的检查器中,添加组件>物理>刚体。将刚体的“拖动"值设置为5。此外,在检查器的“ Box Collider"组件下,选择“ Is Trigger"。

    在多维数据集上创建一个脚本,并将其命名为 collideWithSphere ,打开脚本并删除Start和Update函数,这将使我们能够检测到与Sphere的碰撞。这次不需要他们。输入以下代码:

    using UnityEngine;using System.Collections;public class collideWithSphere : MonoBehaviour{ void OnTriggerEnter(Collider other) { Destroy(other.gameObject); }}

    OnTriggerEnter每当触发对撞机撞到另一个对撞机时都会发送一条消息。在这种情况下,我们要告诉它销毁一切。保存脚本并返回到Unity编辑器。将多维数据集从层次结构拖动到“项目"面板。您会注意到层次结构中的多维数据集文本已变为蓝色。这是因为我们已经创建了一个预制件并将其保存在我们的项目中。现在从层次结构中删除多维数据集。

    我们现在需要的是一个脚本,用于生成多维数据集。在层次结构中,单击创建>创建空白,然后将其重命名为“检查器"中的“游戏管理器",然后向其中添加一个名为gameManager的脚本。打开脚本并添加以下代码:

    using UnityEngine;using System.Collections;public class gameManager : MonoBehaviour { //a variable to hold the prefab we want to spawn public GameObject cube; //we want some variables to decide how any cubes to spawn  //and how high above us we want them to spawn public int numberToSpwan; public float lowestSpawnheight; public float highestSpawnheight;	// Use this for initialization	void Start () { for (int i = 0; i < numberToSpwan; i++) { Instantiate(cube, new Vector3(Random.Range(-9, 9), Random.Range(lowestSpawnheight, highestSpawnheight), 0), Quaternion.identity); } }		// Update is called once per frame	void Update () {		}}

    保存脚本。返回编辑器,在层次结构中选择“游戏管理器",然后将多维数据集预制体从项目面板拖到检查器中的“多维数据集"变量。还要在此处填写生成的值。您可以随意摆弄它,以使其变硬或简单。请注意,将最低的多维数据集生成足够高以允许Uniduino初始化是值得的-在您能够移动之前输掉游戏可能令人沮丧!

    完成的项目

    现在当您按下播放键时,魔方会在您上方生成并掉落。您可以使用电位计来避免它们,并可以使用按钮来减慢时间。

    In this project we have created a custom controller with an Arduino, configured Unity and Uniduino to communicate with it, and created a simple game to test it out. The concepts here can be applied to almost any project, and there are even game jams which specialize in custom controllers.

    使用Arduino和Unity,您几乎可以用任何东西创建自定义控制器。您是否创建了控制飞船的高保真音响?一个控制平台游戏的烤面包机?

    如果您已经创建了这样的项目,我希望看到它!将其发布在下面的评论中!

    标签: Arduino 游戏控制器 游戏开发 编程