需求

新设计的玩法,需要在项目的基本玩法基础上做差异,弱化操作性和刺激感,增加策略性和持续体验感。

具体来说,由败者立即退场改为可无限重生并增加时限、胜利目标不再存活而是资源收集、根据场上玩家优势的高低使用机制进行动态调整等。

然而,因为对于实际游玩体验的预期不够明确,并且对于具体的资源控制策略不好评估(就是菜又爱玩),于是决定模拟一下最终游戏效果来进行观察,大概用掉一天工时。

详细内容

2024-03-08T04:11:43.png
场景及文件结构

using System;
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;
using UnityEngine.UIElements;

public class testPoint : MonoBehaviour
{
    public GameObject point;
    private List<Vector3> position = new List<Vector3>(); // 存储所有点的位置
    private List<GameObject> points = new List<GameObject>(); // 存储所有点的引用
    int seconds;
    public TMP_Text _time;
    [Header("↓系统生成道具坐标")] public string pointPosStr = "-17,-10|17,30|17,-50|-34,20|51,-10|-17,50|0,-60|34,20|17,-10|-51,-10|-17,-50|0,40|0,-20|34,-20|17,50|-34,-40|-34,-20|-51,30|0,0|34,-40|51,30|0,60|-17,-30|51,-30|-34,40|17,-30|-51,-30|-17,10|34,0|0,-40|34,40|0,20|51,10|-34,0|17,10|-51,10|-17,30";
    [Header("↓开局生成道具数")] public int rndTimesFirst;//首次产
    [Header("↓每次生成道具数")] public int rndTimes;//每次产
    [Header("↓最大道具数 超过不自动生成")] public int mostPointNum;//最大产
    [Header("↓生成频率s")] public int generFreq;//生成频率
    [Header("↓死亡后恢复时间s")] public float deathRecoveryTime = 7f; // 死亡后恢复时间
    [Header("↓成为boss最低要求分")] public int minBoss;

    [Header("↓死后掉分道具半径")] public float deathGenRadius;
    [Header("↓被杀失分比例")] public float lostPointRatio;
    [Header("↓杀人者得分比例")] public float takePointRatio;
    // Start is called before the first frame update
    void Start()
    {
        StartCoroutine(pointGener());
        StartCoroutine(TimeCount());
        string[] positionsArray = pointPosStr.Split('|');
        foreach (string pos in positionsArray)
        {
            string[] coords = pos.Split(',');
            float x = Convert.ToSingle(coords[0]);
            float y = Convert.ToSingle(coords[1]);
            position.Add(new Vector3(x, y, -1f));
        }

        GeneratePoints(rndTimesFirst);

    }

    IEnumerator TimeCount()
    {
        while (true)
        {
            if (_time != null)
            {
                seconds += 1;
                if (seconds>=60)
                {
                    _time.text = seconds / 60 + ":" + (seconds % 60);
                }
                else
                {
                    string a = "";
                    if (seconds < 10) a = "0";
                    _time.text = "0:" + a + seconds;
                }
            }

            yield return new WaitForSeconds(1);

        }

    }

    IEnumerator pointGener()
    {
        while (true)
        {
            yield return new WaitForSeconds(generFreq);

            if (points.Count < mostPointNum)
            {
                GeneratePoints(rndTimes);
            }

        }

    }

    // Update is called once per frame
    void Update()
    {
        //if (points.Count < mostPointNum)
        //{
        //    GeneratePoints(rndTimes);
        //    //eatenPointsCount = 0;
        //}



    }

    void GeneratePoints(int count)
    {
        // 随机选择位置生成点
        for (int i = 0; i < count; i++)
        {
            Vector3 randomPos = GetRandomEmptyPosition();
            GameObject newPoint = Instantiate(point, randomPos, Quaternion.identity);
            points.Add(newPoint);
        }
    }
    public void deathGeneratePoints(int count,Vector3 pos)
    {
        Vector2 vec2pos = new Vector2(pos.x, pos.y);
        for (int i = 0; i < count; i++)
        {

            Vector2 rndPos = vec2pos + UnityEngine.Random.insideUnitCircle * deathGenRadius;

            float distanceFromCenterB = rndPos.magnitude;
            if (distanceFromCenterB > 70)
            {
                // 如果在外部,则将其拉回到圆形场地内
                rndPos = rndPos.normalized * (70-6);
            }
            Vector3 rndPos3 = new Vector3(rndPos.x, rndPos.y,-1f);
            GameObject newPoint = Instantiate(point, rndPos3, Quaternion.identity);
            points.Add(newPoint);
        }

    }

    void assignPoint(GameObject obj)
    {

    }

    Vector3 GetRandomEmptyPosition()
    {
        List<Vector3> emptyPositions = new List<Vector3>(position);
        foreach (GameObject point in points)
        {
            emptyPositions.Remove(point.transform.position);
        }
        return emptyPositions[UnityEngine.Random.Range(0, emptyPositions.Count)];
    }

    public void PointEaten(GameObject _obj)
    {
        points.Remove(_obj);
        Destroy(_obj);
        //eatenPointsCount++;
    }

}

public static class listManager
{
    /// <summary>
    /// 获取数组随机N个元素
    /// </summary>
    /// <param name="array">指定数组</param>
    /// <param name="count">获取元素个数</param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public static T[] GetRandomChilds<T>(this T[] array, int count)
    {

        T[] tempArray = new T[array.Length];
        T[] tempResultArray = new T[count];

        array.CopyTo(tempArray, 0);

        tempArray.SortRandom<T>();

        System.Array.Copy(tempArray, tempResultArray, count);

        return tempResultArray;
    }

    /// <summary>
    /// 获取列表随机N个元素
    /// </summary>
    /// <param name="list"></param>
    /// <param name="count"></param>
    /// <typeparam name="T"></typeparam>
    /// <returns></returns>
    public static List<T> GetRandomChilds<T>(this List<T> list, int count)
    {
        List<T> tempList = new List<T>();

        // tempList = list.GetRange(0,list.Count);
        tempList.AddRange(list);
        tempList.SortRandom<T>();
        return tempList.GetRange(0, count);
    }

    /// <summary>
    /// 数组的2个元素位置调换
    /// </summary>
    public static void Swap<T>(this T[] array, int index1, int index2)
    {
        T temp = array[index2];
        array[index2] = array[index1];
        array[index1] = temp;
    }
    /// <summary>
    /// 列表的2个元素位置调换
    /// </summary>
    public static void Swap<T>(this List<T> list, int index1, int index2)
    {
        T temp = list[index2];
        list[index2] = list[index1];
        list[index1] = temp;
    }

    /// <summary>
    /// 乱序排序数组
    /// </summary>
    public static T[] SortRandom<T>(this T[] array)
    {
        int randomIndex;
        for (int i = array.Length - 1; i > 0; i--)
        {
            randomIndex = UnityEngine.Random.Range(0, i);
            array.Swap(randomIndex, i);
        }
        return array;
    }
    /// <summary>
    /// 乱序排序列表
    /// </summary>
    public static List<T> SortRandom<T>(this List<T> list)
    {
        int randomIndex;
        for (int i = list.Count - 1; i > 0; i--)
        {
            randomIndex = UnityEngine.Random.Range(0, i);
            list.Swap(randomIndex, i);
        }
        return list;
    }

}

管理脚本,控制积分生成,下面带了几个数组和list管理函数

using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
using TMPro;
using System.Linq;
using Unity.VisualScripting;
using System;

public class testPointLead : MonoBehaviour
{
    public TMP_Text leaderboardTextPrefab; // UI Text 预制体,用于创建排行榜条目
    public Transform leaderboardParent; // 用于容纳排行榜条目的父对象
    private Dictionary<string, int> playerScores = new Dictionary<string, int>(); // 玩家得分字典
    int minBoss;
    public string bossMark="";
    void Start()
    {
        minBoss = FindObjectOfType<testPoint>().minBoss;
        // 初始化玩家得分
        GameObject[] players = GameObject.FindGameObjectsWithTag("player");
        foreach (GameObject player in players)
        {
            string playerName = player.name;
            playerScores.Add(playerName, 0);
        }

        // 创建排行榜条目
        foreach (string playerName in playerScores.Keys)
        {
            CreateLeaderboardEntry(playerName);
        }
    }

    void Update()
    {
        UpdateLeaderboard(); // 更新排行榜
    }

    void CreateLeaderboardEntry(string playerName)
    {
        TMP_Text leaderboardText = Instantiate(leaderboardTextPrefab, leaderboardParent);
        leaderboardText.text = playerName + " - Score: " + playerScores[playerName];
    }

    void UpdateLeaderboard()
    {
        // 更新排行榜 UI

        List<string> sortedPlayers = playerScores.Keys.OrderByDescending(key => playerScores[key]).ToList();
        for (int i = 0; i < sortedPlayers.Count; i++)
        {
            string playerName = sortedPlayers[i];
            leaderboardParent.GetChild(i).GetComponent<TMP_Text>().text = playerName + " - Score: " + playerScores[playerName];
            if (i == 0 && playerScores[playerName] > minBoss)
            {
                leaderboardParent.GetChild(i).GetComponent<TMP_Text>().fontSize = 30;
                leaderboardParent.GetChild(i).GetComponent<TMP_Text>().color = Color.red;
                bossMark = playerName;
            }
            else if(i == 0 && playerScores[playerName] <= minBoss)
            {
                leaderboardParent.GetChild(i).GetComponent<TMP_Text>().fontSize = 26;
                leaderboardParent.GetChild(i).GetComponent<TMP_Text>().color = Color.white;
                bossMark = "";
            }
        }
        UpdateBossScale();

    }
    public bool IsBoss(string playerName)
    {
        return playerName == bossMark;

    }

    void UpdateBossScale()
    {
        // 检查当前 Boss
        GameObject bossPlayer = GameObject.Find(bossMark);

        if (bossPlayer != null)
        {
            // 缩放变换
            bossPlayer.transform.localScale = new Vector3(3,3,3);
        }

        // 检查是否有新的 Boss 产生,如果有,则恢复之前 Boss 的缩放
        GameObject[] players = GameObject.FindGameObjectsWithTag("player");
        foreach (GameObject player in players)
        {
            if (player.name != bossMark)
            {
                player.transform.localScale = new Vector3(1.5f, 1.5f, 1.5f);// 恢复正常缩放
            }
        }
    }

    // 玩家吃到 point 时调用此方法,更新玩家得分
    public void UpdatePlayerScore(string playerName, int score)
    {
        if (playerScores.ContainsKey(playerName))
        {
            playerScores[playerName] += score;
            if (playerScores[playerName] < 0)
            {
                playerScores[playerName] = 0;
            }

        }
        else
        {
            playerScores.Add(playerName, score);
            CreateLeaderboardEntry(playerName); // 如果新玩家加入,创建对应的排行榜条目
        }
    }

    public int getPoint(string playerName)
    {
        return playerScores[playerName];

    }

}

排行榜处理部分代码

using Scripts.Game.Gen.gen.rawdata;
using System;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEditor;
using UnityEngine;

public class testPointBall : MonoBehaviour
{
    // Start is called before the first frame update

    public GameObject[] players;
    private Transform target;
    private GameObject attacker;

    public float initSpeed;
    float curSpeed;
    public float dirSpeed;
    public float maxSpeed;
    public float ac;

    float lostPointRatio;
    float takePointRatio;
    testPointLead leaderBoard;
    void Start()
    {
        lostPointRatio = FindObjectOfType<testPoint>().lostPointRatio;
        takePointRatio = FindObjectOfType<testPoint>().takePointRatio;
        initBall();
        leaderBoard = FindObjectOfType<testPointLead>();
        players = GameObject.FindGameObjectsWithTag("player");
        StartCoroutine(speedUp());
    }

    // Update is called once per frame
    void Update()
    {
        if (target == null)
        {
            FindPlayer(); // 寻找玩家
        }
        else
        {
            MoveTowardsObject(); // 向最近的B类物体移动

        }

        if (curSpeed > maxSpeed)
        {
            curSpeed = maxSpeed;
        }
    }
    void FindPlayer()
    {

        string bossFind = leaderBoard.bossMark;
        if (bossFind != "" && GameObject.Find(bossFind).GetComponent<testPointPlayer>().isDead!=true &&attacker!=null && attacker.name != bossFind)
        {
            target = GameObject.Find(bossFind).transform;
           
        }
        else {
            List<GameObject> livePlayers = new List<GameObject>(); // 活人
            foreach (GameObject player in players)
            {
                if (player.GetComponent<testPointPlayer>().isDead != true && player != attacker)
                {
                    livePlayers.Add(player);
                }
            }
            int targetKey = UnityEngine.Random.Range(0, livePlayers.Count);
            target = livePlayers[targetKey].transform;
           
        }
        //UpdatePlayerColor();

    }
    void MoveTowardsObject()
    {
        transform.position = Vector3.MoveTowards(transform.position, target.position, curSpeed * Time.deltaTime);
        colliderSim();
    }
    void colliderSim()
    {
        float distanceToT = Vector3.Distance(this.transform.position, target.position);
        if (distanceToT<2.5f)
        {

            float randomValue = UnityEngine.Random.Range(0f, 1f);

            if (randomValue <= target.gameObject.GetComponent<testPointPlayer>().skill)
            {
                lostPoint();
                target.gameObject.GetComponent<testPointPlayer>().Die();
                Debug.Log(target.gameObject.name + "挂了");
                initBall();
            }
            else
            {
                if (target != null) { 
                    attacker = target.gameObject;
                    target = null;
                }
                FindPlayer();
                curSpeed += dirSpeed;
            }
        }
    }

    void lostPoint()
    {
        int targetPoint = leaderBoard.getPoint(target.name);
        int lostPoint = (int)Mathf.Ceil(targetPoint * lostPointRatio);
        if (lostPoint > 0)
        {
            leaderBoard.UpdatePlayerScore(target.name, -lostPoint);

            if (attacker != null)
            {
                int getPoint = (int)Mathf.Ceil(lostPoint * takePointRatio);
                int genPoint = lostPoint - getPoint;
                if (getPoint > 0) { leaderBoard.UpdatePlayerScore(attacker.name, getPoint); }
                if (genPoint > 0) { FindObjectOfType<testPoint>().deathGeneratePoints(genPoint, target.position); }

            }
            else
            {
                FindObjectOfType<testPoint>().deathGeneratePoints(lostPoint, target.position);
            }

        }
    }

    void initBall()
    {
        curSpeed = initSpeed;
        target = null;
        this.transform.position = new Vector3(0, 0, -1);
        attacker = null;
    }

    IEnumerator speedUp()
    {
        while (true)
        {
            yield return new WaitForSeconds(1);
            curSpeed += ac;
        }

    }

    void UpdatePlayerColor()
    {

        if (target != null)
        {
            // 缩放变换
            target.gameObject.GetComponent<MeshRenderer>().material.color = Color.yellow;

            // 检查是否有新的 Boss 产生,如果有,则恢复之前 Boss 的缩放
            foreach (GameObject player in players)
            {
                if (player != target)
                {
                    player.gameObject.GetComponent<MeshRenderer>().material.color = new Color(0.1843137f, 0.3764706f, 0.517647f);// 恢复正常缩放
                }
            }
        }
    }


}

球的逻辑代码

using Game.Font;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.SocialPlatforms.Impl;
public class testPointPlayer : MonoBehaviour
{
    public float speed = 13f; // A类物体移动速度
    private Transform target; // 最近的B类物体
    public float skill; //0-1 越小越强

    //public float[] deathCheckInterval ; // 死亡检查间隔时间随机
    //public int bossIntervalTimes= 2; // boss检查倍数
    float deathRecoveryTime; // 死亡后恢复时间
    public bool isDead = false; // 玩家是否死亡

    void Start()
    {
        deathRecoveryTime = FindObjectOfType<testPoint>().deathRecoveryTime;
        //StartCoroutine(DeathCheck());
    }

    /*IEnumerator DeathCheck()
    {
        while (true)
        {
            int p = 1;
            if (FindObjectOfType<testPointLead>().IsBoss(this.name))
            {
                p = bossIntervalTimes;
                Debug.Log(this.name + "是boss");
            };

            yield return new WaitForSeconds(Random.Range(deathCheckInterval[0] /p, deathCheckInterval[1] / p));

            if (!isDead)
            {
                float randomValue = Random.Range(0f, 1f);
                
                if (randomValue <= skill)
                {
                    Die();
                    Debug.Log(this.name + "挂了");
                }
            }
        }
    }
    */
    public void Die()
    {
        target = null;
        isDead = true;
        GetComponent<Rigidbody>().Sleep();
        StartCoroutine(DeathRecovery());
    }
    IEnumerator DeathRecovery()
    {
        yield return new WaitForSeconds(deathRecoveryTime);
        isDead = false;
        GetComponent<Rigidbody>().WakeUp();
    }



    protected virtual void LateUpdate()
    {
        if (!isDead)
        {
            FindNearestObject(); // 寻找最近的物体
            if (target != null)
            {
                MoveTowardsObject(); // 向最近的B类物体移动
            }
        }
    }

    void FindNearestObject()
    {
        GameObject[] bObjects = GameObject.FindGameObjectsWithTag("point"); // 找到所有的B类物体
        float shortestDistance = Mathf.Infinity;
        GameObject nearestBObject = null;

        foreach (GameObject bObject in bObjects)
        {
            float distanceToB = Vector3.Distance(this.transform.position, bObject.transform.position);
            if (distanceToB < shortestDistance)
            {
                shortestDistance = distanceToB;
                nearestBObject = bObject;
            }
        }

        target = nearestBObject != null ? nearestBObject.transform : null;
    }

    void MoveTowardsObject()
    {
        transform.rotation = Quaternion.identity;
        transform.position = Vector3.MoveTowards(transform.position, target.position, speed * Time.deltaTime);
    }

    void OnCollisionEnter(Collision collision)
    {
        if (collision.gameObject.CompareTag("point"))
        {
            //SendMessage("eatPoint", int.Parse(collision.gameObject.name), SendMessageOptions.RequireReceiver);
            // 碰撞到B类物体时触发事件
            FindObjectOfType<testPoint>().PointEaten(collision.gameObject);
            FindObjectOfType<testPointLead>().UpdatePlayerScore(this.name,1);

        }
    }
}

玩家类,其实是npc类

using System.Collections;
using System.Collections.Generic;
using TMPro;
using Unity.VisualScripting;
using UnityEngine;

public class testPointTruePlayer : testPointPlayer
{
    public TMP_Dropdown dropdown;
    int selected=0;
    public Transform tri;
    public TMP_Dropdown lookDropDown;
    Camera mainCamera;

    void Start()
    {
        mainCamera = Camera.main;
        dropdown.onValueChanged.AddListener(OnDropdownValueChanged);
        lookDropDown.onValueChanged.AddListener(OnLookDropdownValueChanged);
    }

    void OnDropdownValueChanged(int index)
    {

        selected = index;

    }
    void OnLookDropdownValueChanged(int index)
    {
        if (index == 0) 
        { 
            mainCamera.transform.position = new Vector3(0,0,-120);
            mainCamera.transform.rotation =new Quaternion(0,0,0,0);
        }
        else {
            mainCamera.transform.position = new Vector3(0, -85, -35);
            mainCamera.transform.rotation = Quaternion.Euler(-50f, 0, 0);
        }




    }

    void Update()
    {
        if (selected == 0)
        {
            float horizontalInput = Input.GetAxis("Horizontal");
            float verticalInput = Input.GetAxis("Vertical");

            // 计算移动方向
            Vector3 moveDirection = new Vector3(horizontalInput, verticalInput, 0f).normalized;
            transform.rotation = Quaternion.identity;
            // 根据输入移动物体
            transform.Translate(moveDirection * speed * Time.deltaTime, Space.World);
        }
        tri.up = mainCamera.transform.up;
    }

    protected override void LateUpdate()
    {
        if (selected==1)
        {
            base.LateUpdate();
        }

    }
}

主角类,继承了npc的基础上,增加了操控

(决定每当犯懒或者时间有限的时候,就用这种直接贴代码放图片的方式,记录+备忘

标签: unity

添加新评论