炮塔周围的MapCube无法改变颜色,因为,炮塔的触发器很大,会被OnMouseEnter检测到。取消Queries Hit Triggers 即可忽略检测触发器。
取消Queries Hit Triggers 勾,就不会检测触发器了。
炮塔周围的MapCube无法改变颜色,因为,炮塔的触发器很大,会被OnMouseEnter检测到。取消Queries Hit Triggers 即可忽略检测触发器。
取消Queries Hit Triggers 勾,就不会检测触发器了。
不是只有触发器才能触发OnMouseEnter和OnMouseExit 代码有问题:EventSystem.current.IsPointerOverGameObject() 是代表在UI层。
PhysicsManager---Queries Hit Triggers 打勾
using UnityEngine;
using UnityEngine.EventSystems;
public class MapCube : MonoBehaviour
{
[HideInInspector]
public GameObject turrerGo; //保存当前Cube身上的炮台
public GameObject buildEffect;
private Renderer renderer;
private void Start()
{
renderer = GetComponent<Renderer>();
}
public void BuildTurret(GameObject turretPrefab)
{
turrerGo = GameObject.Instantiate(turretPrefab, transform.position, Quaternion.identity);
GameObject effect = GameObject.Instantiate(buildEffect, transform.position, Quaternion.identity);
Destroy(effect, 1.0f);
}
private void OnMouseEnter()
{
if (turrerGo == null && EventSystem.current.IsPointerOverGameObject() == false)
{
renderer.material.color = Color.red;
}
}
private void OnMouseExit()
{
renderer.material.color = Color.white;
}
}
炮台的中心点是这个,围绕这个选择的,导致旋转异常。偏离底座。 解决方法,添加空物体,设置为底座的中心点。 空物体的前方和炮台的前方不一致导致显示异常。解决方法:把空物体的方向设置为炮台的前方。
using System.Collections.Generic;
using UnityEngine;
public class Turret : MonoBehaviour
{
public List<GameObject> enemys = new List<GameObject>();
private void OnTriggerEnter(Collider col)
{
if (col.tag == "Enemy")
{
enemys.Add(col.gameObject);
}
}
private void OnTriggerExit(Collider col)
{
if (col.tag == "Enemy")
{
enemys.Remove(col.gameObject);
}
}
public float attackRateTime = 1.0f; //多少秒攻击一次
private float timer = 0.0f;
public GameObject bulletPrefab;
public Transform firePosition;
public Transform head;
private void Start()
{
timer = attackRateTime;
}
private void Update()
{
//先调整方向,再攻击
if (enemys.Count > 0 && enemys[0] != null)
{
Vector3 targetPosition = enemys[0].transform.position;
targetPosition.y = head.position.y;
head.LookAt(targetPosition);
}
//攻击
timer += Time.deltaTime;
if (enemys.Count > 0 && timer >= attackRateTime)//刚刚实例化出来的时候,攻击就绪,第一次可以攻击
{
timer = 0;
Attack();
}
}
private void Attack()
{
if (enemys[0] == null)
{
UpdateEnemys();
}
if (enemys.Count > 0)
{
GameObject bullet = GameObject.Instantiate(bulletPrefab, firePosition.position, firePosition.rotation);
bullet.GetComponent<Bullet>().SetTarget(enemys[0].transform);
}
else
{
timer = attackRateTime;
}
}
private void UpdateEnemys()
{
//enemys.RemoveAll(null);
List<int> emptyIndex = new List<int>();
for (int index = 0; index < enemys.Count; index++)
{
if (enemys[index] == null)
{
emptyIndex.Add(index);
}
}
for (int i = 0; i < emptyIndex.Count; i++)
{
enemys.RemoveAt(emptyIndex[i] - i);//移除一个元素,索引会减少1,Count会减小。
}
}
}
Start Speed : Random Between Two Constants
速度设置为2-4 之间。
Collision 碰撞体,粒子效果与物体发生碰撞。勾选Collision,选择World。
Dampen 碰撞到物体减少一部分速度。
Gravity Modifier,粒子效果的重力,
using UnityEngine;
public class Enemy : MonoBehaviour
{
public float speed = 10.0f;
public int hp = 150;
public GameObject explosionEffect;
private Transform[] positions;
private int index = 0;
void Start()
{
positions = Waypoints.positions;
}
void Update()
{
Move();
}
private void Move()
{
if (index > positions.Length - 1) return;
transform.Translate((positions[index].position - transform.position).normalized * speed * Time.deltaTime);
if (Vector3.Distance(positions[index].position, transform.position) < 0.2f)
{
index++;
}
if (index > positions.Length - 1)
{
ReachDestination();
}
}
private void ReachDestination()
{
GameObject.Destroy(this.gameObject);
}
private void OnDestroy()
{
EnemySpawner.CountEnemyAlive--;
}
public void TakeDamage(int damage)
{
if (hp <= 0) return;//如果当前敌人hp小于0,死了不作处理
hp -= damage;
if (hp <= 0)
{
Die();
}
}
private void Die()
{
GameObject effect = GameObject.Instantiate(explosionEffect, transform.position, transform.rotation);
Destroy(effect, 1.5f);
Destroy(this.gameObject);
}
}
using System.Collections.Generic;
using UnityEngine;
public class Turret : MonoBehaviour
{
public List<GameObject> enemys = new List<GameObject>();
private void OnTriggerEnter(Collider col)
{
if (col.tag == "Enemy")
{
enemys.Add(col.gameObject);
}
}
private void OnTriggerExit(Collider col)
{
if (col.tag == "Enemy")
{
enemys.Remove(col.gameObject);
}
}
public float attackRateTime = 1.0f; //多少秒攻击一次
private float timer = 0.0f;
public GameObject bulletPrefab;
public Transform firePosition;
private void Start()
{
timer = attackRateTime;
}
private void Update()
{
timer += Time.deltaTime;
if (enemys.Count > 0 && timer >= attackRateTime)//刚刚实例化出来的时候,攻击就绪,第一次可以攻击
{
timer = 0;
Attack();
}
}
private void Attack()
{
if (enemys[0] == null)
{
UpdateEnemys();
}
if (enemys.Count > 0)
{
GameObject bullet = GameObject.Instantiate(bulletPrefab, firePosition.position, firePosition.rotation);
bullet.GetComponent<Bullet>().SetTarget(enemys[0].transform);
}
else
{
timer = attackRateTime;
}
}
private void UpdateEnemys()
{
//enemys.RemoveAll(null);
List<int> emptyIndex = new List<int>();
for (int index = 0; index < enemys.Count; index++)
{
if (enemys[index] == null)
{
emptyIndex.Add(index);
}
}
for (int i = 0; i < emptyIndex.Count; i++)
{
enemys.RemoveAt(emptyIndex[i] - i);//移除一个元素,索引会减少1,Count会减小。
}
}
}
在终点建立炮塔Turret时候,发射子弹Bullet,刚好敌人Enemy进入了终点了,Bullet子弹在图中飞向目标,敌人Enemy进入终点自毁,并没有在炮塔中的记录敌人的List集合移除。发射子弹朝向目标消失。
空指针异常:
①,炮台的记录敌人的List不正常移除,敌人进入终点自动销毁。
②,子弹发射途中,敌人进入终点自动销毁,子弹飞行目标消失卡在途中。(子弹Target消失)
using UnityEngine;
public class Bullet : MonoBehaviour
{
public int damage = 50;
public float speed = 40.0f;
public GameObject explosionEffectPrefab;
private float distanceArriveTarget = 1.2f;
private Transform target;
public void SetTarget(Transform _target)
{
this.target = _target;
}
private void Update()
{
if(target == null)
{
Die();
return;
}
transform.LookAt(target.position);
transform.Translate(Vector3.forward * speed * Time.deltaTime);
Vector3 dir = target.position - transform.position;
if (dir.magnitude < distanceArriveTarget)
{
target.GetComponent<Enemy>().TakeDamage(damage);
Die();
}
}
private void Die()
{
GameObject effect = GameObject.Instantiate(explosionEffectPrefab, transform.position, transform.rotation);
Destroy(effect, 1.0f);
Destroy(this.gameObject);
}
}
Render Mode:Mesh:3D像素风格。
Shape---Sphere 球体就是向四周发散。
Emission下Rate over Time为0是一次发散,Bursts爆发,10-25个。
Size Over LifeTime 这个曲线,衰减。
自定义曲线,在红线上右键,Add Key。
取消Looping 取消循环播放。
给Bullet预制体,指定爆炸效果ExplosionEffect预制体。
刚体,设置成连续检测模式。Collision Detection :Continuous。适合高速运动物体。
Trigger检测不到,使用距离检测。移除Collider和Rigidbody。
using UnityEngine;
public class Bullet : MonoBehaviour
{
public int damage = 50;
public float speed = 40.0f;
public GameObject explosionEffectPrefab;
private float distanceArriveTarget = 1.2f;
private Transform target;
public void SetTarget(Transform _target)
{
this.target = _target;
}
private void Update()
{
transform.LookAt(target.position);
transform.Translate(Vector3.forward * speed * Time.deltaTime);
Vector3 dir = target.position - transform.position;
if (dir.magnitude < distanceArriveTarget)
{
target.GetComponent<Enemy>().TakeDamage(damage);
GameObject effect = GameObject.Instantiate(explosionEffectPrefab, transform.position, transform.rotation);
Destroy(effect, 1.0f);
Destroy(this.gameObject);
}
}
}
using UnityEngine;
public class Enemy : MonoBehaviour
{
public float speed = 10.0f;
private Transform[] positions;
private int index = 0;
void Start()
{
positions = Waypoints.positions;
}
void Update()
{
Move();
}
private void Move()
{
if (index > positions.Length - 1) return;
transform.Translate((positions[index].position - transform.position).normalized * speed * Time.deltaTime);
if (Vector3.Distance(positions[index].position, transform.position) < 0.2f)
{
index++;
}
if (index > positions.Length - 1)
{
ReachDestination();
}
}
private void ReachDestination()
{
GameObject.Destroy(this.gameObject);
}
private void OnDestroy()
{
EnemySpawner.CountEnemyAlive--;
}
public void TakeDamage(int damage)
{
}
}
using UnityEngine;
public class Bullet : MonoBehaviour
{
public int damage = 50;
public float speed = 40.0f;
public GameObject explosionEffectPrefab;
private Transform target;
public void SetTarget(Transform _target)
{
this.target = _target;
}
private void Update()
{
transform.LookAt(target.position);
transform.Translate(Vector3.forward * speed * Time.deltaTime);
}
private void OnTriggerEnter(Collider col)
{
if (col.tag == "Enemy")
{
col.GetComponent<Enemy>().TakeDamage(damage);
GameObject.Instantiate(explosionEffectPrefab, transform.position, transform.rotation);
Destroy(this.gameObject);
}
}
}
using System.Collections.Generic;
using UnityEngine;
public class Turret : MonoBehaviour
{
public List<GameObject> enemys = new List<GameObject>();
private void OnTriggerEnter(Collider col)
{
if (col.tag == "Enemy")
{
enemys.Add(col.gameObject);
}
}
private void OnTriggerExit(Collider col)
{
if (col.tag == "Enemy")
{
enemys.Remove(col.gameObject);
}
}
public float attackRateTime = 1.0f; //多少秒攻击一次
private float timer = 0.0f;
public GameObject bulletPrefab;
public Transform firePosition;
private void Start()
{
timer = attackRateTime;
}
private void Update()
{
timer += Time.deltaTime;
if (enemys.Count > 0 && timer >= attackRateTime)//刚刚实例化出来的时候,攻击就绪,第一次可以攻击
{
timer -= attackRateTime;
Attack();
}
}
private void Attack()
{
GameObject bullet = GameObject.Instantiate(bulletPrefab, firePosition.position, firePosition.rotation);
bullet.GetComponent<Bullet>().SetTarget(enemys[0].transform);
}
}
using UnityEngine;
public class Bullet : MonoBehaviour
{
public int damage = 50;
public float speed = 40.0f;
private Transform target;
public void SetTarget(Transform _target)
{
this.target = _target;
}
private void Update()
{
transform.LookAt(target.position);
transform.Translate(Vector3.forward * speed * Time.deltaTime);
}
}
炮台需要旋转,所以开火位置在炮台上。
using System.Collections.Generic;
using UnityEngine;
public class Turret : MonoBehaviour
{
public List<GameObject> enemys = new List<GameObject>();
private void OnTriggerEnter(Collider col)
{
if (col.tag == "Enemy")
{
enemys.Add(col.gameObject);
}
}
private void OnTriggerExit(Collider col)
{
if (col.tag == "Enemy")
{
enemys.Remove(col.gameObject);
}
}
public float attackRateTime = 1.0f; //多少秒攻击一次
private float timer = 0.0f;
public GameObject bulletPrefab;
public Transform firePosition;
private void Start()
{
timer = attackRateTime;
}
private void Update()
{
timer += Time.deltaTime;
if (enemys.Count > 0 && timer >= attackRateTime)//刚刚实例化出来的时候,攻击就绪,第一次可以攻击
{
timer -= attackRateTime;
Attack();
}
}
private void Attack()
{
GameObject.Instantiate(bulletPrefab, firePosition.position, firePosition.rotation);
}
}
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class BuildManager : MonoBehaviour
{
public TurretData laserTurretData;
public TurretData missileTurretData;
public TurretData standardTurretData;
//表示当前选择的炮台(要建造的炮台)
private TurretData selectedTurretData;
public Text moneyText;
public Animator moneyAnimator;
private int money = 1000;
void ChangeMoney(int change = 0)
{
money += change;
moneyText.text = "¥" + money;
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
if (EventSystem.current.IsPointerOverGameObject() == false)
{
//开发炮台的建造
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
bool isCollider = Physics.Raycast(ray, out hit, 1000, LayerMask.GetMask("MapCube"));
if (isCollider)
{
MapCube mapCube = hit.collider.gameObject.GetComponent<MapCube>();//得到点击的mapCube
if (selectedTurretData != null && mapCube.turrerGo == null)
{
//可以创建
if (money > selectedTurretData.cost)
{
ChangeMoney(-selectedTurretData.cost);
mapCube.BuildTurret(selectedTurretData.turretPrefab);
}
else
{
//TODO 提示钱不够
moneyAnimator.SetTrigger("Flicker");
}
}
else if (mapCube.turrerGo != null)
{
//TODO 升级处理
}
}
}
}
}
public void OnLaserSelected(bool isOn)
{
if (isOn)
{
selectedTurretData = laserTurretData;
}
}
public void OnMissileSelected(bool isOn)
{
if (isOn)
{
selectedTurretData = missileTurretData;
}
}
public void OnStandardSelected(bool isOn)
{
if (isOn)
{
selectedTurretData = standardTurretData;
}
}
}
给炮塔添加触发器,刚体,用刚体来检测,触发器来判断第一个进入的敌人。
给敌人添加标签Tag。
Trigger触发器检测性能优化:Edit---Project Settings---Physics
添加炮塔层,和敌人层,Enemy Layer 和 Turret Layer。
只需要Enemy和Turret做检测。
using System.Collections.Generic;
using UnityEngine;
public class Turret : MonoBehaviour
{
public List<GameObject> enemys = new List<GameObject>();
private void OnTriggerEnter(Collider col)
{
if (col.tag == "Enemy")
{
enemys.Add(col.gameObject);
}
}
private void OnTriggerExit(Collider col)
{
if (col.tag == "Enemy")
{
enemys.Remove(col.gameObject);
}
}
}
把Particle System中的,Renderer下Randerer Mode 改成 Mesh风格,3D风格。
Circle向四周发散。
Rate over Time 每秒发散,改成0发散一次。
加号,一次发散多少个。
灰尘又大到小,散去。Size Over LiftTime。
using UnityEngine;
public class MapCube : MonoBehaviour
{
[HideInInspector]
public GameObject turrerGo; //保存当前Cube身上的炮台
public GameObject buildEffect;
public void BuildTurret(GameObject turretPrefab)
{
turrerGo = GameObject.Instantiate(turretPrefab, transform.position, Quaternion.identity);
GameObject effect = GameObject.Instantiate(buildEffect, transform.position, Quaternion.identity);
Destroy(effect, 1.0f);
}
}
using UnityEngine;
public class MapCube : MonoBehaviour
{
[HideInInspector]
public GameObject turrerGo; //保存当前Cube身上的炮台
public void BuildTurret(GameObject turretPrefab)
{
turrerGo = GameObject.Instantiate(turretPrefab, transform.position, Quaternion.identity);
}
}
给钱Text添加动画(闪烁)
Animation动画选项:快捷键是Ctrl+6
Animation界面,更改颜色,录制变化,启用/禁用关键帧记录模式,Samples越大越快,
在Animation界面内,右键空白,创建空白State,(Create State),
右键Entry设置默认State,Set StateMachine Default State
右键需要切换的State,set as Layer Default State 也可以设置默认State
其他State切换,Make Transition。
取消勾选Flicker的Loop Time,循环播放。
状态切换条件,设置一个参数,Parameters。
设置一个Trigger的参数。
取消Has Exit Time,设置Conditions条件,切换条件
另一条线,Has Exit Time勾选,动画播放结束自动切换到Empty。
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class BuildManager : MonoBehaviour
{
public TurretData laserTurretData;
public TurretData missileTurretData;
public TurretData standardTurretData;
//表示当前选择的炮台(要建造的炮台)
private TurretData selectedTurretData;
public Text moneyText;
public Animator moneyAnimator;
private int money = 1000;
void ChangeMoney(int change = 0)
{
money += change;
moneyText.text = "¥" + money;
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
if (EventSystem.current.IsPointerOverGameObject() == false)
{
//开发炮台的建造
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
bool isCollider = Physics.Raycast(ray, out hit, 1000, LayerMask.GetMask("MapCube"));
if (isCollider)
{
MapCube mapCube = hit.collider.gameObject.GetComponent<MapCube>();//得到点击的mapCube
if (mapCube.turrerGo == null)
{
//可以创建
if (money > selectedTurretData.cost)
{
ChangeMoney(-selectedTurretData.cost);
mapCube.BuildTurret(selectedTurretData.turretPrefab);
}
else
{
//TODO 提示钱不够
moneyAnimator.SetTrigger("Flicker");
}
}
else
{
//TODO 升级处理
}
}
}
}
}
public void OnLaserSelected(bool isOn)
{
if (isOn)
{
selectedTurretData = laserTurretData;
}
}
public void OnMissileSelected(bool isOn)
{
if (isOn)
{
selectedTurretData = missileTurretData;
}
}
public void OnStandardSelected(bool isOn)
{
if (isOn)
{
selectedTurretData = standardTurretData;
}
}
}
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
public class BuildManager : MonoBehaviour
{
public TurretData laserTurretData;
public TurretData missileTurretData;
public TurretData standardTurretData;
//表示当前选择的炮台(要建造的炮台)
private TurretData selectedTurretData;
public Text moneyText;
private int money = 1000;
void ChangeMoney(int change = 0)
{
money += change;
moneyText.text = "¥" + money;
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
if (EventSystem.current.IsPointerOverGameObject() == false)
{
//开发炮台的建造
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
bool isCollider = Physics.Raycast(ray, out hit, 1000, LayerMask.GetMask("MapCube"));
if (isCollider)
{
MapCube mapCube = hit.collider.gameObject.GetComponent<MapCube>();//得到点击的mapCube
if (mapCube.turrerGo == null)
{
//可以创建
if (money > selectedTurretData.cost)
{
ChangeMoney(-selectedTurretData.cost);
mapCube.BuildTurret(selectedTurretData.turretPrefab);
}
else
{
//TODO 提示钱不够
}
}
else
{
//TODO 升级处理
}
}
}
}
}
public void OnLaserSelected(bool isOn)
{
if (isOn)
{
selectedTurretData = laserTurretData;
}
}
public void OnMissileSelected(bool isOn)
{
if (isOn)
{
selectedTurretData = missileTurretData;
}
}
public void OnStandardSelected(bool isOn)
{
if (isOn)
{
selectedTurretData = standardTurretData;
}
}
}
using UnityEngine;
public class MapCube : MonoBehaviour
{
[HideInInspector]
public GameObject turrerGo; //保存当前Cube身上的炮台
public void BuildTurret(GameObject turretPrefab)
{
}
}
using UnityEngine;
using UnityEngine.EventSystems;
public class BuildManager : MonoBehaviour
{
public TurretData laserTurretData;
public TurretData missileTurretData;
public TurretData standardTurretData;
//表示当前选择的炮台(要建造的炮台)
private TurretData selectedTurretData;
public int money = 1000;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
if (EventSystem.current.IsPointerOverGameObject() == false)
{
//开发炮台的建造
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
bool isCollider = Physics.Raycast(ray, out hit, 1000, LayerMask.GetMask("MapCube"));
if (isCollider)
{
MapCube mapCube = hit.collider.gameObject.GetComponent<MapCube>();//得到点击的mapCube
if (mapCube.turrerGo == null)
{
//可以创建
if (money > selectedTurretData.cost)
{
money -= selectedTurretData.cost;
mapCube.BuildTurret(selectedTurretData.turretPrefab);
}
else
{
//TODO 提示钱不够
}
}
else
{
//TODO 升级处理
}
}
}
}
}
public void OnLaserSelected(bool isOn)
{
if(isOn)
{
selectedTurretData = laserTurretData;
}
}
public void OnMissileSelected(bool isOn)
{
if(isOn)
{
selectedTurretData = missileTurretData;
}
}
public void OnStandardSelected(bool isOn)
{
if(isOn)
{
selectedTurretData = standardTurretData;
}
}
}
Add Layer 添加层,射线检测只检测MapCube这一层。
MapCube 层
设置MapCube的Layer为MapCube层。
检查是否在UI层
EventSystem.IsPointerOverGameObject() == true 就是鼠标在UI层
using UnityEngine;
using UnityEngine.EventSystems;
public class BuildManager : MonoBehaviour
{
public TurretData laserTurretData;
public TurretData missileTurretData;
public TurretData standardTurretData;
//表示当前选择的炮台(要建造的炮台)
private TurretData selectedTurretData;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
if(EventSystem.current.IsPointerOverGameObject() == false)
{
//开发炮台的建造
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
RaycastHit hit;
bool isCollider = Physics.Raycast(ray, out hit, 1000, LayerMask.GetMask("MapCube"));
if(isCollider)
{
GameObject mapCube = hit.collider.gameObject;//得到点击的mapCube
}
}
}
}
public void OnLaserSelected(bool isOn)
{
if(isOn)
{
selectedTurretData = laserTurretData;
}
}
public void OnMissileSelected(bool isOn)
{
if(isOn)
{
selectedTurretData = missileTurretData;
}
}
public void OnStandardSelected(bool isOn)
{
if(isOn)
{
selectedTurretData = standardTurretData;
}
}
}
数据类需要序列化[System.Serializable]
using UnityEngine;
[System.Serializable]
public class TurretData
{
public GameObject turretPrefab;
public int cost;
public GameObject turretUpgradePrefab;
public int costUpgraded;
public TurretType type;
}
public enum TurretType
{
LaserTurret,
MissileTurret,
StandardTurret
}
using UnityEngine;
public class BuildManager : MonoBehaviour
{
public TurretData laserTurretData;
public TurretData missileTurretData;
public TurretData standardTurretData;
//表示当前选择的炮台(要建造的炮台)
private TurretData selectedTurretData;
public void OnLaserSelected(bool isOn)
{
if(isOn)
{
selectedTurretData = laserTurretData;
}
}
public void OnMissileSelected(bool isOn)
{
if(isOn)
{
selectedTurretData = missileTurretData;
}
}
public void OnStandardSelected(bool isOn)
{
if(isOn)
{
selectedTurretData = standardTurretData;
}
}
}
using UnityEngine;
public class TurretData
{
public GameObject turretPrefab;
public int cost;
public GameObject turretUpgradePrefab;
public int costUpgraded;
public TurretType type;
}
public enum TurretType
{
LaserTurret,
MissileTurret,
StandardTurret
}
Toggle Group ---单选按钮分组
为什么不能移动,你看看你的输入法,现在是中文状态,怎么移动?
改成英文输入法的状态就可以移动。