如何根据得分更改生成预制体的比例?

huangapple go评论63阅读模式
英文:

How to change scale of spawning prefabs depending on score?

问题

Here is the translation of the provided content:

更新

请允许我以另一种方式组织这个问题。请原谅我,我的先前代码可能有点混乱,我已经做了一些更改。

当玩家与生成的对象发生碰撞时,生成的对象将被销毁,玩家会获得一分。当玩家获得一定数量的分数时(在这种情况下,我们假设是3分),生成的对象将开始变小。

主要目标

玩家获得3分,生成的对象按一小部分比例减小(在这种情况下是0.1)。每当玩家获得3分时,重复这个过程。

当前逻辑

- 游戏管理器 -

首先,让我向您展示在游戏管理器内部跟踪分数的内容。

// 单例
public static GameManager gameManager { get; private set; }

// 如果重复分数=4,则重置为0以重复该过程
public int repeatScoreGoal = 4;

[HideInInspector]
public int repeatScore = 0;

// 在另一个脚本中访问此方法以增加 repeatScore 值
// 当玩家发生碰撞并销毁对象时
public void RepeatScore()
{
    repeatScore += 1;

    if (repeatScore >= repeatScoreGoal)
    {
        repeatScore = 0;
    }
}

- 碰撞检测以增加分数 -

这是附加到玩家的脚本上的方法,用于在玩家与对象发生碰撞时增加分数。

  private const string tagDestructible = "Destructible";

  private void OnCollisionEnter(Collision other)
{

    if (other.gameObject.tag == tagDestructible)
    {
        if(other.gameObject.tag == tagDestructible)
        {
            GameManager.gameManager.RepeatScore();
        }
        
        Destroy(other.gameObject);
    }

- 附加到预制件的预制件比例脚本 -

这次,我创建了一个简单的脚本,附加到预制件本身,而不是在“ObjectSpawner”脚本中调整对象的比例,这样每个预制件都可以具有自己独特的比例。这使我无需在要生成不同对象时重新创建附加到“ObjectSpawner”脚本的空游戏对象。每个预制件的比例将为每个预制件提供独特的值,这将在以后的开发中派上用场。

using UnityEngine;

public class PrefabScale : MonoBehaviour
{
    
    public int recScore = 3;
    private int currentScore;

    private bool objectScale = false;

void Update()
{
    // 引用到单例的变量
    currentScore = GameManager.gameManager.repeatScore;
    
    // 如果 Game Manager 中的 repeatSore >= recScore,则调整对象比例
    if (currentScore >= recScore && objectScale == false)
    {
        objectScale = true;

        if (objectScale == true)
        {
            gameObject.transform.localScale += new Vector3(-0.1f, -0.1f, 
            -0.1f);
        }

        if (currentScore == 0)
        {
            objectScale = false;
        }
    }
}

- 附加到空游戏对象生成预制件的对象生成器脚本 -

现在让我们看看附加到空游戏对象以生成预制件的“ObjectSpawner”脚本。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ObjectSpawn : MonoBehaviour
{
    [SerializeField]
    float[] percentages;

    public GameObject[] prefab;

    public float spawnRate = 1f;
    public float minHeight = -1f;
    public float maxHeight = 1f;

    public int recScore = 3;
    private int currentScore;
    
    // 忽略这些变量,在更新方法的注释中查看更多信息
    /*
    private bool spawnPointPosition = false;
    private float resetSpawnPoint;
    */

private void OnEnable()
{
    InvokeRepeating(nameof(Spawn), spawnRate, spawnRate);
}

private void OnDisable()
{
    CancelInvoke(nameof(Spawn));
}

private void Spawn()
{
    GameObject objects = Instantiate(prefab[GetRandomSpawn()], 
    transform.position,
    Quaternion.identity);
    objects.transform.position += Vector3.up * Random.Range(minHeight, 
    maxHeight);
    objects.transform.SetParent(transform);
}

private int GetRandomSpawn()
{
    float random = Random.Range(0f, 1f);
    float numForAdding = 0;
    float total = 0;
    for (int i = 0; i < percentages.Length; i++)
    {
        total += percentages[i];
    }

    for (int i = 0; i < prefab.Length; i++)
    {
        if (percentages[i] / total + numForAdding >= random)
        {
            return i;
        }
        else
        {
            numForAdding += percentages[i] / total;
        }
    }
    return 0;
}

private void Update()
{
    /* 我将这个单例引用保留在这里,以防我们在解决当前问题时需要它 */
    currentScore = GameManager.gameManager.repeatScore;
    
    

    /* 请忽略下面的内容,这是调整生成预制件的空游戏对象的 x 位置,
      与预制件的比例无关 */
    /*

    resetSpawnPoint = GameManager.gameManager.score;

    if (currentScore >= recScore && spawnPointPosition == false)
    {
        spawnPointPosition = true;

        if (spawnPointPosition == true)
        {
            gameObject.transform.position += new Vector3(0f, 0f, 0f);
        }

    }

    if (currentScore == 0)
    {
        spawnPointPosition = false;
    }

    if (resetSpawnPoint == 0)
    {
        gameObject.transform.position = new Vector3(20f, 0f, 0f);
    }
    */
}

}

当前结果

在当前设置下,当玩家获得所需分数(在这种情况下,我们仍然假设是3分)时,当前生成的对象将缩小

英文:

UPDATE

Allow me to organize this question in another way. Forgive me, my previous code may have been confusing and I have made a few changes.

As the player collides with the objects being spawned, the objects being spawned are destroyed and the player gains a point. When the player earns a certain amount of points (in this case lets say 3) the objects being spawned will begin to get smaller.

The Main Goal

The player gains 3 points, the objects being spawned scale down by a small fraction (in this case 0.1). Repeat this process every time the player gets 3 points.

Current Logic

-Game Manager-

First let me show you what is keeping track of the score inside the Game Manager.

//Singleton
public static GameManager gameManager { get; private set; }

//if repeat score = 4 reset to 0 to repeat the process  
public int repeatScoreGoal = 4;

[HideInInspector]
public int repeatScore = 0;

//access this method in another script to increase repeatScore value
//when they player collides and destroys object
public void RepeatScore()
{
    repeatScore += 1;

    if (repeatScore &gt;= repeatScoreGoal)
    {
        repeatScore = 0;
    }
}

-Collision Detection to Increase Score-

Here is the method on the script attached to the player to increase score when the player collides with the object.

  private const string tagDestructible = &quot;Destructible&quot;;

  private void OnCollisionEnter(Collision other)
{

    if (other.gameObject.tag == tagDestructible)
    {
        if(other.gameObject.tag == tagDestructible)
        {
            GameManager.gameManager.RepeatScore();
        }
        
        Destroy(other.gameObject);
    }

-Prefab Scale Script Attached to Prefab-

This time instead of scaling the object in the "ObjectSpawner" script, I created a simple script to attach to the prefab itself so each prefab can have its own unique scale. This allows me to not have to recreate the empty game object that the "ObjectSpawner" script is attached to when I want to spawn different objects. The scale would change to a unique value for each prefab which will come in handy later down the road.

using UnityEngine;

public class PrefabScale : MonoBehaviour
{
    
    public int recScore = 3;
    private int currentScore;

    private bool objectScale = false;

void Update()
{
    //variable referencing to singleton
    currentScore = GameManager.gameManager.repeatScore;
    
    //if reapeatSore in Game Manager = recScore, scale object
    if (currentScore &gt;= recScore &amp;&amp; objectScale == false)
    {
        objectScale = true;

        if (objectScale == true)
        {
            gameObject.transform.localScale += new Vector3(-0.1f, -0.1f, 
            -0.1f);
        }

        if (currentScore == 0)
        {
            objectScale = false;
        }
    }
}

-Object Spawner Script Attached to Empty Game Object Spawning Prefabs-

Now lets see the "ObjectSpawner" script that is attached to an empty game object spawning the prefabs.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ObjectSpawn : MonoBehaviour
{
    [SerializeField]
    float[] percentages;

    public GameObject[] prefab;

    public float spawnRate = 1f;
    public float minHeight = -1f;
    public float maxHeight = 1f;

    public int recScore = 3;
    private int currentScore;
    
    //ignore these variables, view notes in update method for more info
    /*
    private bool spawnPointPosition = false;
    private float resetSpawnPoint;
    */

private void OnEnable()
{
    InvokeRepeating(nameof(Spawn), spawnRate, spawnRate);
}

private void OnDisable()
{
    CancelInvoke(nameof(Spawn));
}

private void Spawn()
{
    GameObject objects = Instantiate(prefab[GetRandomSpawn()], 
    transform.position,
    Quaternion.identity);
    objects.transform.position += Vector3.up * Random.Range(minHeight, 
    maxHeight);
    objects.transform.SetParent(transform);
}

private int GetRandomSpawn()
{
    float random = Random.Range(0f, 1f);
    float numForAdding = 0;
    float total = 0;
    for (int i = 0; i &lt; percentages.Length; i++)
    {
        total += percentages[i];
    }

    for (int i = 0; i &lt; prefab.Length; i++)
    {
        if (percentages[i] / total + numForAdding &gt;= random)
        {
            return i;
        }
        else
        {
            numForAdding += percentages[i] / total;
        }
    }
    return 0;
}

private void Update()
{
    /*I left this singleton reference here just incase we need it for 
      the current problem we are trying to sovle */
    currentScore = GameManager.gameManager.repeatScore;
    
    

    /*ignore this below, this is adjusting the x position of the 
      empty game object that is spawning the prefabs and does not relate 
      to the scaling of the prefabs*/
    /*

    resetSpawnPoint = GameManager.gameManager.score;

    if (currentScore &gt;= recScore &amp;&amp; spawnPointPosition == false)
    {
        spawnPointPosition = true;

        if (spawnPointPosition == true)
        {
            gameObject.transform.position += new Vector3(0f, 0f, 0f);
        }

    }

    if (currentScore == 0)
    {
        spawnPointPosition = false;
    }

    if (resetSpawnPoint == 0)
    {
        gameObject.transform.position = new Vector3(20f, 0f, 0f);
    }
    */
}

}

Current Results

With this current setup, When the player earns the required score (in this case we are still just going to say 3) the objects that are currently spawned will scale.

However, the objects that spawn after that scale are the original size of the prefab until the player hits the required score again. I'm looking for a way to retain that scale with the newly spawned objects and for this entire process to repeat every time the player gets 3 points.

What is currently happening:

The game starts, the objects scale is 1. The player destroys 3 objects earning 3 points. All objects on the screen scale to 0.9. All objects being spawned after are back to scale 1.

I am looking for:

The game starts, the player gets 3 points, objects on screen scale to 0.9. Newly spawned objects are also scale 0.9. the player gets another 3 points. objects on screen are now scale to 0.8. Newly spawned objects are also scaled to 0.8. The process just keeps going.

I hope this clears some things up! Thank you all so much for your time and input!

答案1

得分: 1

这是可能的:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ObjectSpawn : MonoBehaviour
{
    [SerializeField]
    float[] percentages;

    public GameObject[] prefab;

    public float spawnRate = 1f;
    public float minHeight = -1f;
    public float maxHeight = 1f;

    public int recScore = 3;
    private int currentScore;

    private List<GameObject> spawnedObjects = new List<GameObject>(); // 存储已生成对象的引用
    private List<GameObject> newSpawnedObjects = new List<GameObject>(); // 存储新生成对象的引用
    private bool isScaling = false; // 用于跟踪是否正在进行缩放
    private bool isNewSpawnScaled = false; // 用于跟踪新生成的对象是否已缩放

    private void OnEnable()
    {
        InvokeRepeating(nameof(Spawn), spawnRate, spawnRate);
    }

    private void OnDisable()
    {
        CancelInvoke(nameof(Spawn));
    }

    private void Spawn()
    {
        GameObject spawnedObject = Instantiate(prefab[GetRandomSpawn()], transform.position, Quaternion.identity);
        spawnedObject.transform.position += Vector3.up * Random.Range(minHeight, maxHeight);
        spawnedObject.transform.SetParent(transform);

        spawnedObjects.Add(spawnedObject);
        newSpawnedObjects.Add(spawnedObject);

        if (currentScore >= recScore && !isScaling && !isNewSpawnScaled)
        {
            StartCoroutine(ScaleSpawnedObjects());
        }
    }

    private Vector3 GetScaleFactor()
    {
        // 定义要应用的缩放因子
        Vector3 scaleFactor = new Vector3(0.9f, 0.9f, 0.9f);
        return scaleFactor;
    }

    private int GetRandomSpawn()
    {
        // ...(您现有的代码)
    }

    private IEnumerator ScaleSpawnedObjects()
    {
        isScaling = true;

        Vector3 scaleFactor = GetScaleFactor();

        foreach (GameObject spawnedObject in spawnedObjects)
        {
            if (spawnedObject != null)
            {
                spawnedObject.transform.localScale = Vector3.Scale(spawnedObject.transform.localScale, scaleFactor);
                yield return null; // 在缩放下一个对象之前等待下一帧
            }
        }

        foreach (GameObject newSpawnedObject in newSpawnedObjects)
        {
            if (newSpawnedObject != null)
            {
                newSpawnedObject.transform.localScale = Vector3.Scale(newSpawnedObject.transform.localScale, scaleFactor);
            }
        }

        isNewSpawnScaled = true; // 设置标志以指示新生成的对象已经被缩放
        isScaling = false;
    }

    private void Update()
    {
        currentScore = GameManager.gameManager.repeatScore;

        if (currentScore >= recScore && !isScaling && !isNewSpawnScaled)
        {
            StartCoroutine(ScaleSpawnedObjects());
        }

        if (currentScore == 0)
        {
            isNewSpawnScaled = false; // 当分数达到0时重置标志
            newSpawnedObjects.Clear(); // 清除新生成对象的列表
        }
    }
}

更新:当currentScore大于或等于recScore时,将调用ScaleSpawnedObjects方法以缩放现有对象。当currentScore小于recScore时,将调用ResetScale方法以重置对象的初始缩放。

英文:

This should be possible:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectSpawn : MonoBehaviour
{
[SerializeField]
float[] percentages;
public GameObject[] prefab;
public float spawnRate = 1f;
public float minHeight = -1f;
public float maxHeight = 1f;
public int recScore = 3;
private int currentScore;
private List&lt;GameObject&gt; spawnedObjects = new List&lt;GameObject&gt;(); // Store references to the spawned objects
private List&lt;GameObject&gt; newSpawnedObjects = new List&lt;GameObject&gt;(); // Store references to newly spawned objects
private bool isScaling = false; // Flag to track if scaling is in progress
private bool isNewSpawnScaled = false; // Flag to track if newly spawned objects have been scaled
private void OnEnable()
{
InvokeRepeating(nameof(Spawn), spawnRate, spawnRate);
}
private void OnDisable()
{
CancelInvoke(nameof(Spawn));
}
private void Spawn()
{
GameObject spawnedObject = Instantiate(prefab[GetRandomSpawn()], transform.position, Quaternion.identity);
spawnedObject.transform.position += Vector3.up * Random.Range(minHeight, maxHeight);
spawnedObject.transform.SetParent(transform);
spawnedObjects.Add(spawnedObject);
newSpawnedObjects.Add(spawnedObject);
if (currentScore &gt;= recScore &amp;&amp; !isScaling &amp;&amp; !isNewSpawnScaled)
{
StartCoroutine(ScaleSpawnedObjects());
}
}
private Vector3 GetScaleFactor()
{
// Define the scaling factor you want to apply
Vector3 scaleFactor = new Vector3(0.9f, 0.9f, 0.9f);
return scaleFactor;
}
private int GetRandomSpawn()
{
// ... (your existing code)
}
private IEnumerator ScaleSpawnedObjects()
{
isScaling = true;
Vector3 scaleFactor = GetScaleFactor();
foreach (GameObject spawnedObject in spawnedObjects)
{
if (spawnedObject != null)
{
spawnedObject.transform.localScale = Vector3.Scale(spawnedObject.transform.localScale, scaleFactor);
yield return null; // Wait for the next frame before scaling the next object
}
}
foreach (GameObject newSpawnedObject in newSpawnedObjects)
{
if (newSpawnedObject != null)
{
newSpawnedObject.transform.localScale = Vector3.Scale(newSpawnedObject.transform.localScale, scaleFactor);
}
}
isNewSpawnScaled = true; // Set the flag to indicate that newly spawned objects have been scaled
isScaling = false;
}
private void Update()
{
currentScore = GameManager.gameManager.repeatScore;
if (currentScore &gt;= recScore &amp;&amp; !isScaling &amp;&amp; !isNewSpawnScaled)
{
StartCoroutine(ScaleSpawnedObjects());
}
if (currentScore == 0)
{
isNewSpawnScaled = false; // Reset the flag when the score reaches 0
newSpawnedObjects.Clear(); // Clear the list of newly spawned objects
}
}
}

UPDATE: When the currentScore is greater than or equal to the recScore, the ScaleSpawnedObjects method is called to scale the existing objects.
When the currentScore is less than the recScore, the ResetScale method is called to reset the scale of the objects to the initial scale.

答案2

得分: 0

已更新,根据评论从OP进行了编辑,以使用Getter/Setter来简化scale如何更新并应用于新的和活动的objects的方法。

// 在更改缩放之前的目标分数
private int targetScore = 3;

// 用于在getter/setter中使用的currentScore的变量
private int currentScore = 0;

// 用于currentScore的Getter/Setter,如果currentScore在重置currentScore之前达到targetScore,则会更新对象的缩放
private int CurrentScore
{
    get { return currentScore; }
    set
    {
        currentScore = value;

        if (currentScore == targetScore)
        {
            scale -= 0.1f * Vector3.one;

            for (int x = 0; x < objects.Count; x++)
            {
                objects[x].transform.localScale = scale;
            }

            currentScore = 0;
        }
    }
}

// 用于在Getter/Setter中修改的当前缩放的变量,并且可以在生成新对象时使用
private Vector3 scale = Vector3.one;

// 用于循环遍历进行缩放更改的对象列表
private List<GameObject> objects = new List<GameObject>();

// 简化的Spawn方法,设置当前缩放并将其添加到用于将来更新的列表中
private void Spawn()
{
    GameObject newObject = Instantiate(prefab[GetRandomSpawn()], transform.position, Quaternion.identity);
    newObject.transform.position += Vector3.up * Random.Range(minHeight, maxHeight);
    newObject.transform.SetParent(transform);
    newObject.transform.localScale = scale;

    objects.Add(newObject);
}

// 用法
private void Update()
{
    if (DidWhateverItIsThatScores)
    {
        CurrentScore++;
    }
}

使用Getter/Setters非常适合这个用例,要了解更多信息,请参阅文档

注意:您将希望限制缩放,以使其具有最小缩放并且不会变为负数。

英文:

Updated after comments from OP

Edited the script to use a Getter/Setter for the currentScore to simplify how the scale is updated and applied to new and active objects

// Target score before the scale is changed
private int targetScore = 3;
// var to hold the currentScore for use in the getter/setter
private int currentScore = 0;
// Getter/Setter for the currentScore that will update the objects scale if the currentScore reaches the targetScore before resetting the currentScore
private int CurrentScore
{
get { return currentScore; }
set
{
currentScore = value;
if (currentScore == targetScore)
{
scale -= 0.1f * Vector3.one;
for (int x = 0; x &lt; objects.Count; x++)
{
objects[x].transform.localScale = scale;
}
currentScore = 0;
}
}
}
// Var for the current scale that will be modified in the Getter/Setter and can be used when spawning a new object
private Vector3 scale = Vector3.one;
// List of objects to loop through for scale changes
private List&lt;GameObject&gt; objects = new List&lt;GameObject&gt;();
// Simplified Spawn method that sets the current scale and adds it to the list for future updates
private void Spawn()
{
GameObject newObject = Instantiate(prefab[GetRandomSpawn()], transform.position,Quaternion.identity);
newObject.transform.position += Vector3.up * Random.Range(minHeight, maxHeight);
newObject.transform.SetParent(transform);
newObject.transform.localScale = scale;
objects.Add(newObject);
}
// Usage
private void Update()
{
if (DidWhateverItIsThatScores)
{
CurrentScore++;
}
}

Getter/Setters are perfect for this for more info see Docs

Note: You will want to clamp the scale so it has a minimum scale and doesn't go negative.

huangapple
  • 本文由 发表于 2023年6月6日 14:29:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/76411964.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定