unity io抢分玩法模拟 记录
需求
新设计的玩法,需要在项目的基本玩法基础上做差异,弱化操作性和刺激感,增加策略性和持续体验感。
具体来说,由败者立即退场改为可无限重生并增加时限、胜利目标不再存活而是资源收集、根据场上玩家优势的高低使用机制进行动态调整等。
然而,因为对于实际游玩体验的预期不够明确,并且对于具体的资源控制策略不好评估(就是菜又爱玩),于是决定模拟一下最终游戏效果来进行观察,大概用掉一天工时。
详细内容
场景及文件结构
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的基础上,增加了操控
(决定每当犯懒或者时间有限的时候,就用这种直接贴代码放图片的方式,记录+备忘