简体   繁体   中英

Is there a way to write a method to call 'AddForce' to either a Rigidbody or a Rigidbody2D parameter?

I'm trying to make a class that works with both 2D and 3D rigidbodies in Unity. Everything works fine until I get to the 'addForce' method where I get an InvalidCastException, since I'm either casting a 2D to 3D or vice versa. For those of you who don't know, both Rigidbody and Rigidbody2D have the 'addForce' method.

I've looked into the dynamic type (rather not use since it will not work on ios), generics, and interfaces, but still can't come to a solution. Would appreciate any help. Thanks.

public class Scroller : MonoBehaviour {

    ScrollController _scrollController;
    Object _rb;

    public enum SCROLL_MODE {
        TRANSLATION,
        PHYSICS_2D = 2,
        PHYSICS_3D = 3
    }

    public SCROLL_MODE scrollMode = SCROLL_MODE.TRANSLATION;

    // Start is called before the first frame update
    void Start()    {
        _scrollController = ScrollController.Instance;

        switch (scrollMode) {
            case SCROLL_MODE.TRANSLATION:
                break;
            case SCROLL_MODE.PHYSICS_2D:
                _rb = GetComponent<Rigidbody2D>();
                break;
            case SCROLL_MODE.PHYSICS_3D:
                _rb = GetComponent<Rigidbody>();
                break;
        }

        if (!_scrollController)
            logFatalError("Error: There is no ScrollController within the scene.");
    }

    void Update() {
        if (scrollMode == SCROLL_MODE.TRANSLATION)
            transform.Translate(_scrollController.scrollDirection * _scrollController.scrollSpeed * Time.deltaTime);
    }

    void FixedUpdate() {
        if (scrollMode == SCROLL_MODE.PHYSICS_2D || scrollMode == SCROLL_MODE.PHYSICS_3D) {
            if (!_rb) {
                logFatalError("Error: " + gameObject.name + " has no Rigidbody" + scrollMode.ToString("d") + "D");
                return;
            }

            applyScrollForce(_rb);
        }
    }

    void applyScrollForce(Object rb) {
        ((Rigidbody)rb).AddForce(_scrollController.scrollDirection * _scrollController.scrollSpeed * Time.fixedDeltaTime);
    }

    void logFatalError(string msg) {
        Debug.Log(msg);

        enabled = false;
    }
}

Generics won't help because there is surprisingly no common parent class with AddForce. Rigidbody and Rigidbody2D both inherit from Component.

I see you as having two options:

Firstly, and most simply, only use Rigidbody and use constraints to keep it in 2D when needed.

In the case where this isn't an option, a simple 'if' statement would do:

void applyScrollForce(Object rb) {
    var force = _scrollController.scrollDirection * _scrollController.scrollSpeed * Time.fixedDeltaTime;
    if(rb.GetType() == typeof(Rigidbody))
        ((Rigidbody)rb).AddForce(force);
    else if(rb.GetType() == typeof(Rigidbody2D))
        ((Rigidbody2D)rb).AddForce(force);
}

Naturally you probably want to cache the type instead of checking and casting every frame.

Although both Rigibody and Rigidbody2D both have AddForce methods, not only are their signatures different, but there's no relationship between a Rigidbody and a Rigidbody2D.

You can check the documentation and see that they both inherit from the Component class, but that's as far as their similarities end.

You could the switch command on a base Component though, to find out which of the two you're dealing with. A switch statement for two items is a bit of an overkill, but it's neat, and not too heavy on performance. There's also a lot less casting going on.

Component _c = null;

void Start ( )
{
    _scrollController = ScrollController.Instance;

    switch ( scrollMode )
    {
        case SCROLL_MODE.PHYSICS_2D:
            _c = GetComponent<Rigidbody2D> ( );
            break;
        case SCROLL_MODE.PHYSICS_3D:
            _c = GetComponent<Rigidbody> ( );
            break;
    }

    if ( !_scrollController )
        logFatalError ( "Error: There is no ScrollController within the scene." );
}


void applyScrollForce ( )
{
    if ( _c == null )
    {
        Debug.LogWarning ( "The Component is null." );
        return;
    }

    var force = _scrollController.scrollDirection * _scrollController.scrollSpeed * Time.fixedDeltaTime;
    switch ( _c )
    {
        case Rigidbody rb:
            rb.AddForce ( force );
            break;

        case Rigidbody2D rb2D:
            rb2D.AddForce ( force );
            break;

        default:
            Debug.LogError ( $"Component {_c.name} is neither a Rigidbody or Rigidbody2D."  );
            return;
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM