首页 > 解决方案 > 播放器没有响应 Unity 中的 arduino 代码

问题描述

所以我正在开发这个游戏(作为更大游戏的一部分),玩家必须在进入下一个级别之前收集所有硬币。我希望播放器在收到来自 arduino 的一定数量的按钮点击时。每个级别都需要不同数量的压力机。例如,级别 1 需要点击 1 次,级别 2 需要点击 2 次等。这里的问题是玩家没有响应输入,我不知道为什么。当我使用空格键测试这段代码时,播放器移动了,所以问题肯定在播放器和 arduino 之间的某个地方。此外,有时我按下按钮两次,Arduino 输出 3 而不是 2,有时我按下按钮一次,Arduino 输出 2 而不是 1。任何关于如何解决这些问题的提示将不胜感激。

这是 Arduino 脚本:

  #include <Bounce2.h>
#include<SoftwareSerial.h>
#include <Wire.h>

// Connect each button with one connection
// to GND and the other to a digital pin.
const byte buttonPin = 2;
const byte buttonPin2 = 3;

class Button{

  private:

    byte m_buttonPin;
    byte m_counter = 0;
    unsigned long m_buttonPressTimeout;
    unsigned long m_previousMillis;

  public:

    Button(byte buttonPin):
      m_buttonPin(buttonPin),
      m_counter(0),
      m_buttonPressTimeout(2000), // Button press timeout in ms.
      m_previousMillis(0){}

    void Update(){
      if(m_counter > 0 && millis() - m_previousMillis >= m_buttonPressTimeout)
      {
        //Serial.print("Count from Update() just before it's reset to 0 = ");
        Serial.println(GetCounter());
        m_counter = 0;
      }
    }

    void IncrementCounter(){
      m_counter++;
      if(m_counter > 4){m_counter = 4;}
      if(m_counter == 1)
      {
        m_previousMillis = millis();
      }
    }

    friend void IncrementCounter(Button&);

    void IncrementCounter(Button&){
      IncrementCounter();
    }

    byte GetCounter(){
      return m_counter;
    }

};

Bounce buttonOneDebouncer = Bounce();
Bounce buttonTwoDebouncer = Bounce();
Button ButtonOne(buttonPin);
Button ButtonTwo(buttonPin2);

void setup(){
  Serial.begin(9600);
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(buttonPin2, INPUT_PULLUP);
  buttonOneDebouncer.attach(buttonPin);
  buttonTwoDebouncer.attach(buttonPin2);
  buttonOneDebouncer.interval(25);
  buttonTwoDebouncer.interval(25);
}

void loop(){

  // Call the Update function as fast as possible.
  ButtonOne.Update();
  ButtonTwo.Update();

  // Button one pressed.
  if(buttonOneDebouncer.update()){
    if(buttonOneDebouncer.fell()){
      if(digitalRead(buttonPin2) == 0){
        ButtonOne.IncrementCounter();
      }
    }
  }

  // Button two pressed.
  if(buttonTwoDebouncer.update()){
    if(buttonTwoDebouncer.fell()){
      if(digitalRead(buttonPin) == 0){
        ButtonOne.IncrementCounter(ButtonTwo);
      }
    }
  }
  /*if(digitalRead(buttonPin) == 0 && (digitalRead(buttonPin2) == 0))
  {
  Serial.println("9");
  }*/

}

这是播放器脚本:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using System.IO.Ports;

public class PlayerControllerb : MonoBehaviour
{
    public static int level;
    [Header("Debug")]
    [SerializeField] private PlatformProvider platformProvider;
    SerialPort sp = new SerialPort("\\\\.\\COM4", 9600);
    //player = GameObject.FindWithTag("Player").GetComponent<Renderer>().material;
    private void Awake()
    {
        OnSceneLoaded();
        SceneManager.sceneLoaded -= OnSceneLoaded;
        SceneManager.sceneLoaded += OnSceneLoaded;
        print(level);
    }
    public void OnSceneLoaded()
    {

    }
    public void OnSceneLoaded(Scene scene, LoadSceneMode mode)
    {
        platformProvider = FindObjectOfType<PlatformProvider>();
    }
    public float Speed = 1;
    private int currentPlatformIndex;
    public Vector2 height;
    public float xMin, xMax, yMin, yMax;

    string value;
    int amount;

    void Start()
    {
        if (!sp.IsOpen)
        { // If the erial port is not open 
            sp.Open(); // Open 
        }
        sp.ReadTimeout = 1; // Timeout for reading

        print(level);
    }

    void Update()
    {

        // if (Input.GetKeyDown(KeyCode.Space) && level == 1)
        //     GoToNextPlatform();

        if (sp.IsOpen)
        { // Check to see if the serial port is open 
            try
            {

                value = sp.ReadLine(); //Read the information
                amount = int.Parse(value);
                print(amount);
                //transform.Translate(Speed * Time.deltaTime, 0f, 0f);  //walk

                //if (/*amount > 25f)*/Input.GetKeyDown(KeyCode.Space))  //jump

                if (amount == 1 && level == 1)
                {
                    //if (Input.GetKeyDown(KeyCode.Space))
                    //{
                    GoToNextPlatform();
                    // GetComponent<Rigidbody2D>().AddForce(2* height, ForceMode2D.Impulse);
                }
                if (amount == 2 && level == 2)
                {
                    //if (Input.GetKeyDown(KeyCode.Space))
                    //{
                    GoToNextPlatform();
                    // GetComponent<Rigidbody2D>().AddForce(2* height, ForceMode2D.Impulse);
                }
                if (amount == 3 && level == 3)
                {
                    //if (Input.GetKeyDown(KeyCode.Space))
                    //{
                    GoToNextPlatform();
                    // GetComponent<Rigidbody2D>().AddForce(2* height, ForceMode2D.Impulse);
                }
                if (amount == 4 && level == 4)
                {
                    //if (Input.GetKeyDown(KeyCode.Space))
                    //{
                    GoToNextPlatform();
                    // GetComponent<Rigidbody2D>().AddForce(2* height, ForceMode2D.Impulse);
                }
            }
            catch (System.Exception)
            {
            }
            /*GetComponent<Rigidbody2D>().position = new Vector3
            (
                Mathf.Clamp(GetComponent<Rigidbody2D>().position.x, xMin, xMax),
                Mathf.Clamp(GetComponent<Rigidbody2D>().position.y, yMin, yMax)
            );*/

        }
    }
    [ContextMenu(nameof(GoToNextPlatform))]
    public void GoToNextPlatform()
    {
        currentPlatformIndex++;
        currentPlatformIndex = Mathf.Min(currentPlatformIndex, platformProvider.platforms.Length);

        GoToPlatform(currentPlatformIndex);
    }
    [ContextMenu(nameof(GoToPreviousPlatform))]
    public void GoToPreviousPlatform()
    {
        currentPlatformIndex--;
        currentPlatformIndex = Mathf.Max(currentPlatformIndex, 0);

        GoToPlatform(currentPlatformIndex);
    }
    private void GoToPlatform(int index)
    {
        //StopAllCoroutines();
        //StartCoroutine(MoveTo(platformProvider.platforms[index].transform.position));

        gameObject.transform.position = platformProvider.platforms[index].transform.position;
    }
    IEnumerator MoveRoutine(Vector3 target)
    {
        while (!Mathf.Approximately(0, Vector3.Distance(transform.position, target)))
        {
            yield return new WaitForFixedUpdate();
            GetComponent<Rigidbody>().MovePosition(Vector3.MoveTowards(GetComponent<Rigidbody>().position, target, Time.deltaTime * Speed));
        }
    }

    private void FixedUpdate()
    {
        GetComponent<Rigidbody2D>().position = new Vector3

                   (
                        Mathf.Clamp(GetComponent<Rigidbody2D>().position.x, xMin, xMax),
                        Mathf.Clamp(GetComponent<Rigidbody2D>().position.y, yMin, yMax)
                    );
    }
    void ApplicationQuit()
    {
        if (sp != null)
        {

            {
                sp.Close();
            }
        }
    }

}

这是控制台的图片,显示输入正在注册 控制台。这是游戏级别之一的图像:游戏

标签: c#unity3d

解决方案


那么在某个地方,您需要从 yes 获得这些平台的位置。

我会PlatformProvider在每个场景中都有一个专门的

public class PlatformProvider : MonoBehaviour
{
    // Here reference your platforms via drag&drop in the order you need
    public Platform[] platforms;
}

并将其放在所有平台上

public class Platform : MonoBehaviour { }

它不必做任何事情,但用于引用它们。

然后在您的控制器中找到平台,例如每次加载新场景时。

我只是在这里假设您的控制器永远不会被破坏而是保持活动状态,否则您只需要PlatformProvider在 Awake 或通过 Inspector 设置一次,而根本不需要在运行时设置它。

public class PlayerControllerb : MonoBehaviour
{
    [Header("Debug")]
    [SerializeField] private PlatformProvider platformProvider;

    private void Awake ()
    {
        OnSceneLoaded();

        SceneManager.sceneLoaded -= OnSceneLoaded;
        SceneManager.sceneLoaded += OnSceneLoaded;
    }

    private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
    {
        // If anyway there is a new PlayerControllerb on each scene 
        // remove Awake and OnSceneLoaded and simply set the reference in the Inspector.
        platformProvider = FindObjectOfType<PlatformProvider>();
    }

那么您可以简单地从平台获取下一个位置,例如

    // Set in the Inspector in Units per second
    public float Speed = 1;

    private int currentPlatformIndex;

    private void Update()
    {
        if(ARDUINO_MAGIC)
        {
            GoToNextPlatform ();
        }
    }

    // Using this you can debug it in the context menu of the component without Arduino ;)
    [ContextMenu(nameof(GoToNextPlatform)>]
    public void GoToNextPlatform()
    {
        currentPlatformIndex++;
        currentPlatformIndex = Mathf.Min(currentPlatformIndex, platformProvider.platforms.Length - 1); 

        GoToPlatform (currentPlatformIndex);
    } 

    [ContextMenu (nameof(GoToPreviousPlatform))]
    public void GoToPreviousPlatform()
    {
        currentPlatformIndex--;
        currentPlatformIndex = Mathf.Max(currentPlatformIndex, 0);

        GoToPlatform (currentPlatformIndex);
    }

然后对于运动,我会使用 aCoroutine而不是在Update. 无论如何,只要Rigdbody(2D)涉及 a ,您不应该通过组件分配任何转换,Transform而是通过Rigidbody(2D)and分配FixedUpdate,以免破坏物理。在协程中,这可以通过使用来实现WaitForFixedUpdate

    private void GoToPlatform(int index)
    {
        // Depends on your needs
        // You can either use a bool and ignore concurrent routines
        // or simply stop any running one
        StopAllCoroutines();
        StartCoroutine(MoveTo(platformProvider.platforms [index].transform.position));
    }

    private IEnumerator MoveRoutine (Vector3 target)
    {
        while(!Mathf.Approximately(0, Vector3.Distance(transform.position, target)))
        {
            yield return new WaitForFixedUpdate();

            rigidbody.MovePositio (Vector3.MoveTowards(rigidbody.position, target, Time.deltaTime * moveSpeed);
        }
    }
}

推荐阅读