在射线广播的末端/命中点上为移动对象创建粒子

发布于 2025-01-30 07:52:20 字数 1006 浏览 3 评论 0原文

preamble:

我有一个3D侧滚动风格的游戏,玩家在避免避开事物的情况下飞来飞去。

我想在玩家接近(在预设的危险区内,说1.6单位(米))时向播放器添加效果(粒子系统),就像在他们下面的灰尘拖拉云一样。我熟悉粒子系统和射线广播,但我不知道如何将这些概念结合在一起以实现自己的目标。地形随机起伏,如果有帮助,则不是平坦的表面。

我也希望使粒子系统“成长”,如果有道理的话,玩家越接近地形。还可以考虑速度,因此靠近地面并更快地应对粒子系统产生影响。

我的想法:

我已经有一个得分乘数,它使用射线播放来检查玩家从地面/地形检查播放器的位置,并增加了他们获得的近距离。

void Update() {

    force = speed *2;

    RaycastHit hit;
    Ray downRay = new Ray(transform.position, -Vector3.up);

    if (Physics.Raycast(downRay, out hit, dangerZone)) {

        var distanceToGround = hit.distance;
        float hazardMultiplier = Mathf.Round( (transform.position.y - distanceToGround)*100 ) /100;

        if (hit.collider.tag == "Terrain") {
            playerData.scoreMulitplier = hazardMultiplier;
        }
    } 
    
    else {
        playerData.scoreMulitplier = playerData.baseScoreMulitplier;
    }
}

我认为我可以使用射线播放,我已经必须在地形上/在射线播放点上实例化粒子系统,但是我不确定该如何处理。

任何帮助将受到赞赏,并提前感谢。

Preamble:

I have a 3D side scroller style game in which the player flies along avoiding stuff, you know, side-scrollery things.

I'd like to add an effect (particle system) to the player when they get close (within a preset dangerZone, say 1.6 units (meters)) to the terrain, like a dusty dragging cloud under them sort of thing. I'm familiar with the particle system and raycasts but I don't know how to marry the concepts together to achieve what I'm after. The terrain undulates randomly and is not a flat surface, if that helps.

I'd also be hoping to make the particle system 'grow' the closer the player gets to the terrain if that makes sense. There is also speed to consider, so the closer to the ground and faster the player is should have an effect on the particle system.

My Thoughts:

I already have a score multiplier that uses a raycast to check the player's position from the ground/terrain and increases the closer they get.

void Update() {

    force = speed *2;

    RaycastHit hit;
    Ray downRay = new Ray(transform.position, -Vector3.up);

    if (Physics.Raycast(downRay, out hit, dangerZone)) {

        var distanceToGround = hit.distance;
        float hazardMultiplier = Mathf.Round( (transform.position.y - distanceToGround)*100 ) /100;

        if (hit.collider.tag == "Terrain") {
            playerData.scoreMulitplier = hazardMultiplier;
        }
    } 
    
    else {
        playerData.scoreMulitplier = playerData.baseScoreMulitplier;
    }
}

I'm thinking I can use the raycast I already have to instantiate a particle system on the terrain/at the raycast hit point but I'm not sure how exactly to go about this.

Any help is appreciated and thanks in advance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

超可爱的懒熊 2025-02-06 07:52:20

您在正确的轨道上。在我们深入研究解决方案之前,首先要进行几件事:

  • raycast是在固定框架(物理,固定程序)上计算的,而不是视觉框架(更新)。虽然您可以在更新期间调用射线播放,但直到下一个固定帧都不会进行计算。我建议将其移动到固定的设备,以减少逻辑加倍的机会(2个同时射线广播)或跳过逻辑(无射线播放)。

  • 您可以将粒子系统的缩放模式设置为层次结构,并使用转换进行缩放。另外,您可以设置 preshitlesystem的主要属性。。由于您想更改颗粒系统的大小以相当频繁地更改,因此我建议将缩放模式更改为层次结构,并仅修改其附加到的对象的变换。

[SerializeField]
ParticleSystem ps;

[SerializeField]
float dangerZone = 1.6f;

[SerializeField]
float maxParticleSize = 2.0f; //How much you want particles to scale up based on closeness to terrain

void FixedUpdate() {
    RaycastHit hit;
    Ray downRay = new Ray(transform.position, -Vector3.up);

    //Consider adding a layerMask parameter here that only interacts with the "terrain" layer. This will save you physics computing power, and remove the need for tag checking every frame (which is not efficient).
    if (Physics.Raycast(downRay, out hit, dangerZone)) {

        var distanceToGround = hit.distance;
        float hazardMultiplier = Mathf.Round( (transform.position.y - distanceToGround)*100 ) /100; //This doesn't make too much sense to me, so you may want to revisit this. Since this isn't scoped for this question we can ignore it.

        //If you use a layerMask, then you wouldn't ever need to check 
        if (hit.collider.tag == "Terrain") {
            playerData.scoreMulitplier = hazardMultiplier;
            float particleScale = 1 + (dangerZone - hit.distance) / dangerZone;
            ps.transform.localScale = (particleScale, particleScale, particleScale);
            if(ps.isStopped)
                ps.Play();
        } else //In scenarios where the raycast hits a non-terrain target, you'll need to turn off particles. If you use a layermask this wouldn't be needed.
        {
            if(ps.isPlaying)
                ps.Stop();
        }
    } 
    
    else {
        if(ps.isPlaying)
            ps.Stop();
        playerData.scoreMulitplier = playerData.baseScoreMulitplier;
    }
}

You're on the right track. A couple things first before we dive into the solution:

  • Raycasts are calculated on the fixed frame (physics, FixedUpdate), not the visual frame (Update). While you may invoke a Raycast during Update, it won't be calculated until the next FixedFrame anyways. I'd recommend moving this to FixedUpdate to reduce the chance of doubled logic (2 simultaneous raycasts) or skipped logic (no raycasts).

  • You can set your particle system's scaling mode to the hierarchy, and scale using the transform. Alternatively, you can set the startSize of the particleSystem's main attributes.. Since you want to change the size of the particleSystem to change fairly frequently, I would recommend changing the scaling mode to hierarchy and just modifying the transform of the object it is attached to.

[SerializeField]
ParticleSystem ps;

[SerializeField]
float dangerZone = 1.6f;

[SerializeField]
float maxParticleSize = 2.0f; //How much you want particles to scale up based on closeness to terrain

void FixedUpdate() {
    RaycastHit hit;
    Ray downRay = new Ray(transform.position, -Vector3.up);

    //Consider adding a layerMask parameter here that only interacts with the "terrain" layer. This will save you physics computing power, and remove the need for tag checking every frame (which is not efficient).
    if (Physics.Raycast(downRay, out hit, dangerZone)) {

        var distanceToGround = hit.distance;
        float hazardMultiplier = Mathf.Round( (transform.position.y - distanceToGround)*100 ) /100; //This doesn't make too much sense to me, so you may want to revisit this. Since this isn't scoped for this question we can ignore it.

        //If you use a layerMask, then you wouldn't ever need to check 
        if (hit.collider.tag == "Terrain") {
            playerData.scoreMulitplier = hazardMultiplier;
            float particleScale = 1 + (dangerZone - hit.distance) / dangerZone;
            ps.transform.localScale = (particleScale, particleScale, particleScale);
            if(ps.isStopped)
                ps.Play();
        } else //In scenarios where the raycast hits a non-terrain target, you'll need to turn off particles. If you use a layermask this wouldn't be needed.
        {
            if(ps.isPlaying)
                ps.Stop();
        }
    } 
    
    else {
        if(ps.isPlaying)
            ps.Stop();
        playerData.scoreMulitplier = playerData.baseScoreMulitplier;
    }
}

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文