Unity开发实战:从零打造移动端肉鸽游戏
Unity开发实战:从零打造移动端肉鸽游戏
本文将详细介绍使用Unity引擎开发一款类似《吸血鬼幸存者》的肉鸽游戏的实战经验。文章将从游戏管理器、对象池管理器、无限地图、升级系统等核心组件入手,提供详细的代码示例,并介绍如何为移动端制作虚拟摇杆,以及如何将游戏导出到安卓、WebGL和微信小程序等不同平台。
一、游戏管理器
GameManager游戏管理器通过单例模式管理着游戏的各个方面,比如游戏存活的倒计时、玩家信息(血条、经验值、击杀数等)、游戏用到的预制体等。
public class GameManager : MonoBehaviour
{
public static GameManager instance;
[Header("# Game Control")]
public bool isLive;
public float gameTime;
public float maxGameTime = 2 * 10f;
[Header("# Player Info")]
public int playerId;
public float health;
public float maxHealth = 100;
public int level;
public int kill;
public int exp;
public int[] nextExp = { 10, 30, 60, 100, 150, 210, 280, 360, 450, 600 };
[Header("# Game Object")]
public PoolManager pool;
public Player player;
public LevelUp uiLevelUp;
public Result uiResult;
public Transform uiJoy;
public GameObject enemyCleaner;
}
GameManager脚本会变成齿轮的图标,作为小白第一次知道这个功能。
二、对象池管理器
PoolManager用于节省内存资源,防止内存碎片化,通过统一管理游戏对象的生成和销毁来实现。
public class PoolManager : MonoBehaviour
{
public GameObject[] prefabs;
List<GameObject>[] pools;
void Awake()
{
pools = new List<GameObject>[prefabs.Length];
for (int index = 0; index < pools.Length; index++)
{
pools[index] = new List<GameObject>();
}
}
public GameObject Get(int index)
{
GameObject select = null;
foreach (GameObject item in pools[index])
{
if (!item.activeSelf)
{
select = item;
select.SetActive(true);
break;
}
}
if (!select)
{
select = Instantiate(prefabs[index],transform);
pools[index].Add(select);
}
return select;
}
}
三、无限地图
无限地图的原理是给玩家设置一个比游戏窗口大的矩形框作为触发器,当触发器触碰到地图的边界后地图位置就会发生移动来实现无限地图的效果。
public class Reposition : MonoBehaviour
{
Collider2D coll;
void Awake()
{
coll = GetComponent<Collider2D>();
}
void OnTriggerExit2D(Collider2D collision)
{
if (!collision.CompareTag("Area"))
return;
Vector3 playerPos = GameManager.instance.player.transform.position;
Vector3 myPos = transform.position;
switch (transform.tag)
{
case "Ground":
float diffX = playerPos.x - myPos.x;
float diffY = playerPos.y - myPos.y;
float dirX = diffX < 0 ? -1 : 1;
float dirY = diffY < 0 ? -1 : 1;
diffX = Mathf.Abs(diffX);
diffY = Mathf.Abs(diffY);
if (diffX > diffY)
{
transform.Translate(Vector3.right * dirX * 40);
}
else if (diffX < diffY)
{
transform.Translate(Vector3.up * dirY * 40);
}
break;
case "Enemy":
if (coll.enabled)
{
Vector3 dist = playerPos - myPos;
Vector3 ran = new Vector3(Random.Range(-3, 3), Random.Range(-3, 3), 0);
transform.Translate(ran + dist * 2);
}
break;
}
}
}
四、升级系统
当玩家经验进度条满后会对道具进行升级属性的加成。比如说当前武器枪的等级为1,属性值射速为5;伤害为3,当玩家选择给枪进行升级等级为2,枪的属性值射速就变成了6;伤害为3.5。关于道具属性值升级的数据记录在ItemData中。
[CreateAssetMenu(fileName = "Item",menuName = "Scriptable Object/ItemData")]
public class ItemData : ScriptableObject
{
public enum ItemType
{
Melee,
Range,
Glove,
Shoe,
Heal
}
[Header("# Main Info")]
public ItemType itemType;
public int itemId;
public string itemName;
[TextArea]
public string itemDesc;
public Sprite itemIcon;
[Header("# Level Data")]
public float baseDamage;
public int baseCount;
public float[] damages;
public int[] counts;
[Header("# Weapon")]
public GameObject projectile;
public Sprite hand;
}
public class Item : MonoBehaviour
{
public ItemData data;
public int level;
public Weapon weapon;
public Gear gear;
Image icon;
Text textLevel;
Text textName;
Text textDesc;
void Awake()
{

icon = GetComponentsInChildren<Image>()[1];
icon.sprite = data.itemIcon;
Text[] texts = GetComponentsInChildren<Text>();
textLevel = texts[0];
textName = texts[1];
textDesc = texts[2];
textName.text = data.itemName;
}
void OnEnable()
{
textLevel.text = "Lv." + (level + 1);
switch (data.itemType)
{
case ItemData.ItemType.Melee:
case ItemData.ItemType.Range:
textDesc.text = string.Format(data.itemDesc,data.damages[level] * 100,data.counts[level]);
break;
case ItemData.ItemType.Glove:
case ItemData.ItemType.Shoe:
textDesc.text = string.Format(data.itemDesc,data.damages[level] * 100);
break;
default:
textDesc.text = string.Format(data.itemDesc);
break;
}
}
//选择其中一个道具升级,根据ItemData进行属性加成
public void OnClick()
{
switch (data.itemType)
{
case ItemData.ItemType.Melee:
case ItemData.ItemType.Range:
if (level == 0)
{
GameObject newWeapon = new GameObject();
weapon = newWeapon.AddComponent<Weapon>();
weapon.Init(data);
}
else
{
float nextDamage = data.baseDamage;
int nextCount = 0;
nextDamage += data.baseDamage * data.damages[level];
nextCount += data.counts[level];
weapon.LevelUp(nextDamage,nextCount);
}
level++;
break;
case ItemData.ItemType.Glove:
case ItemData.ItemType.Shoe:
if (level == 0)
{
GameObject newGear = new GameObject();
gear = newGear.AddComponent<Gear>();
gear.Init(data);
}
else
{
float nextRate = data.damages[level];
gear.LevelUp(nextRate);
}
level++;
break;
case ItemData.ItemType.Heal:
GameManager.instance.health = GameManager.instance.maxHealth;
break;
}
if (level == data.damages.Length)
{
GetComponent<Button>().interactable = false;
}
}
}
五、虚拟摇杆的制作
在Unity中制作虚拟摇杆需要导入Input System和On-Screen Controls。导入后新建Image,将贴图资源放上去,然后将Stick拖到新建好的Image里(这里改名为Joy)。调整好放置位置后,可以通过调整Movement Range数值来控制虚拟摇杆的活动范围。
六、导出安卓系统apk文件
要导出apk文件,需要确保Unity的安卓模块已安装好。打开File -> Build Settings,切换到Android,根据实际情况进行设置后点击生成按钮,即可导出apk文件。
七、导出WebGL
导出WebGL的流程与导出apk类似,但可能会遇到一些问题。参考其他开发者的文章可以解决这些问题。
八、导出微信小程序
导出微信小程序需要参考相关教程,配置CDN并将游戏资源上传到微信云开发控制台上。发布微信小程序还需要提交材料并经过审核,审核通过后才能上架到微信小程序。
通过本文的介绍,读者可以了解从游戏开发到发布到不同平台的完整流程。希望对Unity开发者有所帮助。