【问题标题】:How to change material on a gameobject by rotation?如何通过旋转改变游戏对象上的材质?
【发布时间】:2021-12-08 23:51:04
【问题描述】:

我想根据平台(游戏对象)旋转在 2 种材质之间进行切换。 这是我到目前为止所做的:

public class PlatformSpawner : MonoBehaviour
{
    public GameObject platform;
    public Material[] platformMaterial;
    Material currentMaterial;
    Renderer _renderer;
}

    void Start()
    {
        _renderer = this.GetComponent<Renderer>();
    }

这个我也写了,但是不想按按钮换材质:

    public void LeftTurn()
    {
        _renderer.material = platformMaterial[0];
        currentMaterial = _renderer.material;
    }
    public void RightTurn()
    {
        _renderer.material = platformMaterial[1];
        currentMaterial = _renderer.material;
    }
}

这是平台向左或向右随机旋转 90 度的地方:

    public struct SpawnPoint
    {
        public Vector3 position;
        public Quaternion orientation;

        public void Step(float distance)
        {
            if (Random.value < 0.5)
            {
                position.x += distance;
                orientation = Quaternion.Euler(0, 90, 0); //change to one of the materials
            }
            else
            {
                position.z += distance;
                orientation = Quaternion.Euler(0, 0, 0); //change to the other of the materials.
             //This is where I want to material to switch.
             //When the objects position changes, the material changes too.
            }
        }
    }

有一张游戏画面。我想改变所有角落平台的材质以获得漂亮的曲线视图。

谁能帮助我在这种情况下该怎么做?我有点迷路了。 非常感谢您的每一次帮助!

编辑:新代码看起来像这样。唯一的问题是 Unity 给了我 15 个错误(见下图),即使 Visual Studio 说没有发现问题。错误与开关有关。

public class PlatformSpawner : MonoBehaviour
{
    public GameObject platform;
    public Transform lastPlatform;
    SpawnPoint _spawn;
    bool stop;

    public Material straightMaterial;
    public Material turnLeftMaterial;
    public Material turnRightMaterial;
    public Renderer roadPrefab;

    [System.Serializable]

    public struct SpawnPoint
    {
       public Vector3 position;
        public Quaternion orientation;
        public RoadType type;

        public enum RoadType
        {
            Straight,
            LeftTurn,
            RightTurn
        }

        private enum Direction
        {
            Z,
            X,
        }

        private Direction lastDirection;

        public void Step(float distance)
        {
            type = RoadType.Straight;

            if (Random.value < 0.5f)
            {
                position.x += distance;
                orientation = Quaternion.Euler(0, 90, 0);
                if (lastDirection == Direction.Z)
                {
                    type = RoadType.RightTurn;
                }
            }
            else
            {
                position.z += distance;
                orientation = Quaternion.Euler(0, 0, 0);
                if (lastDirection == Direction.X)
                {
                    type = RoadType.LeftTurn;
                }

                lastDirection = Direction.Z;
            }
        }
    }

    void Start()
    {
        _spawn.position = lastPlatform.position;
        _spawn.orientation = transform.rotation;

        StartCoroutine(SpawnPlatforms());
    }

    IEnumerator SpawnPlatforms()
    {
        while (!stop)
        {
            var _spawn = new SpawnPoint();

            for (var i = 0; i < 20; i++)
            {
                var newPlatform = Instantiate(roadPrefab, _spawn.position, _spawn.orientation);

                _spawn.Step(1.5f);

                var roadMaterial = _spawn.type switch
                {
                    SpawnPoint.RoadType.LeftTurn => turnLeftMaterial,
                    SpawnPoint.RoadType.RightTurn => turnRightMaterial,
                    _ => straightMaterial
                };

                newPlatform.GetComponent<Renderer>().material = roadMaterial;

                yield return new WaitForSeconds(0.1f);
            }
        }
    }
}

【问题讨论】:

  • 嗨,欢迎来到 SO。当前正在发生的事情,与您希望发生的事情有何不同
  • 嘿,杰,谢谢你的回答!从字面上看,什么都没有发生。我玩过这个,但到目前为止没有成功。我在问题中附上了一张图片,以更好地说明我真正想要的是什么。
  • 上面发布的代码不会产生所示的错误。请编辑问题以包含minimal reproducible example
  • 另外,为什么你的 while 循环中嵌套了一个 for 循环,并且还在 while 的每次迭代开始时创建一个新的生成点?当然你会想使用var _spawn = new SpawnPoint(); while(true){ var newPlatform /* ... */ yield return new WaitForSeconds(0.1f); }
  • 我已经尝试了一切,它仍然是一个完整的 ***... 使用随机材料随处生成的平台。我放弃了。

标签: c# android unity3d mesh gameobject


【解决方案1】:

所以听起来你基本上有一个工作系统来切换材料并生成你的道路部件和材料已经根据你的旋转看起来正确 - 现在你只需要识别曲线。

其实这很简单:

  • 是X方向的当前部分,下一个将在Z方向->左转
  • 是Z方向的当前部分,下一个将在X -> RightTurn
  • 任何其他情况都是直的

所以你可能会做类似的事情

public struct SpawnPoint
{
    public Vector3 position;
    public Quaternion orientation;
    public RoadType type;

    public enum RoadType
    {
        Straight,
        LeftTurn,
        RightTurn
    }

    private enum Direction
    {
        // since your orientation by default equals the Z direction use that as default value for the first tile
        Z,
        X, 
    }
    private Direction lastDirection;

    public void Step(float distance)
    {
        type = RoadType.Straight;

        if (Random.value < 0.5f)
        {
            position.x += distance;
            orientation = Quaternion.Euler(0, 90, 0);

            if(lastDirection == Direction.Z)
            {
                type = RoadType.RightTurn;
            }

            lastDirection = Direction.X;
        }
        else
        {
            position.z += distance;
            orientation = Quaternion.Euler(0, 0, 0); 

            if(lastDirection == Direction.X)
            {
                type = RoadType.LeftTurn;
            }

            lastDirection = Direction.Z;
        }
    }
}

你没有显示你的生成代码,但我会假设类似

public class Example : MonoBehaviour
{
    public Material straightMaterial;
    public Material turnLeftMaterial;
    public Material turnRightMaterial;

    public Renderer roadPrefab;

    private void Awake()
    {
        var spawnPoint = new SpawnPoint();
        
        for(var i = 0; i < 20; i++)
        {
            var roadTile = Instantiate(roadPrefab, spawnPoint.position, spawnPoint.orientation);

            // do the Step after spawning the current tile but before assigning the material
            // -> we want/need to know already where the next tile is going to be
            spawnPoint.Step(1f);
            
            var roadMaterial = spawnPoint.type switch
            {
                SpawnPoint.RoadType.LeftTurn => turnLeftMaterial,
                SpawnPoint.RoadType.RightTurn => turnRightMaterial,
                _ => straightMaterial
            };

            roadTile.GetComponent<Renderer>().material = roadMaterial;
        }
    }
}

看看我的绘画技巧;)

【讨论】:

  • 哇,你真的为这个答案付出了所有努力!非常感谢你!我在这段代码中唯一无法解决的问题是开关。 Visual Studio 没有问题,但 Unity 给了我 15 个缺少括号和分号的错误。我更新了问题中的代码,也许你看到了我没有看到的东西。
  • @imbruceter 你在_renderer; 之后有一个结束}
  • 我决定使用不同的开关,现在它正在工作,但它确实做了一些奇怪的事情:DDDD 请参阅:ibb.co/Y05NjVc
  • @imbruceter 您使用的是哪个 Unity 版本?您可能有一个使用 c# 版本的旧版本,其中尚不支持 switch 模式匹配。在这种情况下,您可以将该部分更改为 Material roadMaterial = null; switch(spawnPoint.type){ case SpawnPoint.RoadType.LeftTurn: roadMaterial = turnLeftMaterial; break; case SpawnPoint.RoadType.RightTurn: roadMaterial = turnRightMaterial; break; default: roadMaterial = straightMaterial; break; }
  • 我做了同样的事情,100%。其他原因会导致问题。材质应该从第 4 个平台开始更改,因为我将 3 个平台预制件放置到场景中以填充相机视图。因此,我制作了一个公开的 Transform lastPlatform,因此平台生成仅从最后播放的平台开始。但除此之外,还必须是其他一些问题。我猜材料确实会在各种角度上发生变化,但我无法调试它。
【解决方案2】:

这将使您开始使用Quaternion.Dot

using UnityEngine;

[RequireComponent(typeof(Renderer))]
public class NewBehaviourScript : MonoBehaviour
{
    public Material Material1;
    public Material Material2;
    public Vector3 Euler = new(90, 0, 0);
    private Renderer _renderer;

    private void Start()
    {
        _renderer = GetComponent<Renderer>();
        _renderer.material = Material1;
    }

    private void Update()
    {
        var dot = Quaternion.Dot(transform.rotation, Quaternion.Euler(Euler));

        if (Mathf.Approximately(dot, 1.0f))
        {
            _renderer.material = Material2;
        }
        else
        {
            _renderer.material = Material1;
        }
    }
}

对 N、E、S、W 角使用不同的材料:

using UnityEngine;

[RequireComponent(typeof(Renderer))]
public class NewBehaviourScript : MonoBehaviour
{
    public Material Material1;
    public Material Material2;
    public Material Material3;
    public Material Material4;
    public Vector3 Euler1 = new(0, 0, 0);
    public Vector3 Euler2 = new(0, 90, 0);
    public Vector3 Euler3 = new(0, 180, 0);
    public Vector3 Euler4 = new(0, 270, 0);
    private Renderer _renderer;

    private void Start()
    {
        _renderer = GetComponent<Renderer>();
        _renderer.material = Material1;
    }

    private void Update()
    {
        if (Mathf.Approximately(Quaternion.Dot(transform.rotation, Quaternion.Euler(Euler1)), 1.0f))
        {
            _renderer.material = Material1;
        }

        if (Mathf.Approximately(Quaternion.Dot(transform.rotation, Quaternion.Euler(Euler2)), 1.0f))
        {
            _renderer.material = Material2;
        }

        if (Mathf.Approximately(Quaternion.Dot(transform.rotation, Quaternion.Euler(Euler3)), 1.0f))
        {
            _renderer.material = Material3;
        }

        if (Mathf.Approximately(Quaternion.Dot(transform.rotation, Quaternion.Euler(Euler4)), 1.0f))
        {
            _renderer.material = Material4;
        }
    }
}

确保将旋转环绕超过 360 度,否则它总是看起来是黄色的(第 4 种材质)。

【讨论】:

  • 这太棒了!看起来它会起作用的。你能告诉我最后一件事吗?我打算用另一种方式来做,只为角落平台附加新材料,但你的方法只处理两个。让我解释。有3个方向:左,右,直。通过这种方法,只能连接左右。见:postimg.cc/F78XnR8X你知道怎么解决吗?
  • 用更多的角落检查我的更新。
  • 感谢您的编辑!这不会真的像那样工作,因为当平台移动到不同的位置时我必须改变材料。您知道如何在这部分代码中实现材料“转换器”吗?:postimg.cc/njXLNzp8
  • 嗯...通过复制或多或少相同的逻辑我猜?至于如何构建代码,请编辑您的问题并发布stackoverflow.com/help/minimal-reproducible-example,因为 sn-ps 还不够简单。
  • 我认为不使用Quaternion也可以达到同样的效果,但只需比较角度Mathf.Approximately(Vector3.Angle(transform.right, Vector3.right), 0)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多