建立如图所示的项目结构,将其中的导出设置依次设置其中四个scenes
作为ReusableObject的上层接口供给ReusableObject挂在Gameobject上面
virtual和abstract都是用来修饰父类的,通过覆盖父类的定义,让子类重新定义。
(1)virtual修饰的方法必须有实现(哪怕是仅仅添加一对大括号),而abstract修饰的方法一定不能实现。
(2)virtual可以被子类重写,而abstract必须被子类重写。
(3)如果类成员被abstract修饰,则该类前必须添加abstract,因为只有抽象类才可以有抽象方法。
(4)无法创建abstract类的实例,只能被继承无法实例化。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public inteface IReusable
{
//生成时调用的方法
void OnSpawn();
//删除时调用的方法
void OnUnSpawn();
}
/*
* @Author: Tongz
* @Date: 2022-07-25 15:32:33
* @LastEditors: Tongz
* @LastEditTime: 2022-07-25 16:05:24
* @FilePath: \undefinedd:\Unity\luobo\Assets\Game\Scripts\Reusableobject.cs
* @Description: [email protected]
*
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public abstract class Reusableobject : MonoBehaviour,IReusable
{
//定义抽象类和抽象方法,子类必须对其进行重写
public abstract void OnSpawn();
public abstract void OnUnSpawn();
}
作为各个物体的子池,提供了Spawn,UnSpawn和UnSpawnAll方法
instantiate是对场景中已经存在的的对象进行一份复制的方法,resources.load和instantiate的区别是第一个GameObject prefab的Resources.Load是“解释”你要加载的东西是个GameObject并且加载到内存中。第二个GameObject instance是对prefab进行一个深度copy克隆到场景中然后从Instantiate的返回值中持有他的引用。
/*
* @Author: Tongz
* @Date: 2022-07-25 16:30:46
* @LastEditors: Tongz
* @LastEditTime: 2022-07-26 14:49:36
* @FilePath: \luobo\Assets\Game\Scripts\Subpool.cs
* @Description: [email protected]
*
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Text;
public class Subpool
{
//声明prefab以及集合作为对象池
GameObject m_prefab;
List m_objects = new List();
//构造函数为m_prefab赋值
public Subpool(GameObject prefab)
{
m_prefab=prefab;
}
//子池中Gameobj的生成方法
public GameObject Spawn()
{
GameObject go = null;
foreach (GameObject item in m_objects)
{
if(!item.activeSelf)
{
go = item;
break;
}
}
if(go == null)
{
go = GameObject.Instantiate(m_prefab);
m_objects.Add(go);
}
go.SetActive(true);
go.SendMessage("OnSpawn",SendMessageOptions.DontRequireReceiver);
return go;
}
//移除子池中一个gameobject
public void Unspawn(GameObject go)
{
if(m_objects.Contains(go))
{
go.SetActive(false);
go.SendMessage("OnUnSpawn",SendMessageOptions.DontRequireReceiver);
}
}
//移除子池中所有的gameobject
public void UnspawnAll()
{
foreach (GameObject item in m_objects)
{
if(item.activeSelf)
{
Unspawn(item);
}
}
}
//供给上层使用验证是否包含gameobj
public bool Contains(GameObject go )
{
return m_objects.Contains(go);
}
}
作为总池来管理子池,统筹subpool并且能动态创建subpool,实现指哪打哪的在内存池中生成物体和销毁物体的效果
/*
* @Author: Tongz
* @Date: 2022-07-26 14:54:32
* @LastEditors: Tongz
* @LastEditTime: 2022-07-26 15:47:06
* @FilePath: \luobo\Assets\Game\Scripts\ObjectPool.cs
* @Description: [email protected]
*
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObjectPool : MonoBehaviour
{
//字典存储子池和他对应的名字
Dictionary m_pools = new Dictionary();
//资源相对于Resources目录所在的的相对位置
public string ResourceDir = "";
public GameObject Spawn(string name)
{
//若无子池创建子池
if(!m_pools.ContainsKey(name))
{
CreateSubpool(name);
}
//调用子池方法生成物体
return m_pools[name].Spawn();
}
public void CreateSubpool(string name)
{
//先令path为空
string path = null;
//判断相对路径是否为空,设定目录位置
if(string.IsNullOrEmpty(ResourceDir))
path = name;
else
path = ResourceDir + "/" + name;
GameObject prefab = Resources.Load(path);
Subpool subpool = new Subpool(prefab);
m_pools.Add(name,subpool);
}
//采取这种方法的目的是要取到目标的subpool并且调用其中方法所以逐个subpool遍历
public void Unspawn(GameObject go)
{
Subpool subpool = null;
foreach (Subpool item in m_pools.Values)
{
if(item.Contains(go))
{
subpool=item;
break;
}
}
subpool.Unspawn(go);
}
//直接逐个释放每个subpool
public void UnspawnAll()
{
foreach (Subpool item in m_pools.Values)
{
item.UnspawnAll();
}
}
}
之前框架的里面都写过了不明白了回去看这里就不解释了
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SingletonMono : MonoBehaviour where T : MonoBehaviour
{
private static T instance;
public static T GetInstance()
{
return instance;
}
protected virtual void Awake()
{
instance = this as T;
}
}
/*
* @Author: Tongz
* @Date: 2022-07-26 17:54:04
* @LastEditors: Tongz
* @LastEditTime: 2022-07-26 18:41:59
* @FilePath: \luobo\Assets\Scripts\MusicMgr.cs
* @Description: [email protected]
*
*/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MusicMgr : SingletonMono
{
//分开播放背景音乐和效果音乐
public string ResourceDir ="";
public AudioSource m_BgMusic;
public AudioSource m_EffectMusic;
protected override void Awake()
{
base.Awake();
m_BgMusic = this.gameObject.AddComponent();
m_BgMusic.loop=true;
m_BgMusic.playOnAwake=false;
m_EffectMusic = this.gameObject.AddComponent();
}
public float BgVolume
{
get{return m_BgMusic.volume;}
set{m_BgMusic.volume = value;}
}
public float EffectVolume
{
get{return m_EffectMusic.volume;}
set{m_EffectMusic.volume = value;}
}
public void PlayBgMusic(string name)
{
//先检查音乐有没有在播放,若没有在播放则更换切片后播放,注意路径问题
string oldName;
//先取得在播放的Audioname 判断是否正在播放 若正在播放则不播放
if(m_BgMusic.clip == null)
oldName="";
else
oldName =m_BgMusic.clip.name;
if(oldName!=name)
{
//先令path为空
string path = null;
//判断相对路径是否为空,设定目录位置
if(string.IsNullOrEmpty(ResourceDir))
path = name;
else
path = ResourceDir + "/" + name;
AudioClip clip=Resources.Load(path);
m_BgMusic.clip=clip;
m_BgMusic.Play();
}
}
public void StopBgMusic()
{
m_BgMusic.Stop();
m_BgMusic.clip = null;
}
public void PlayEffectMusic(string name)
{
//先令path为空
string path = null;
//判断相对路径是否为空,设定目录位置
if(string.IsNullOrEmpty(ResourceDir))
path = name;
else
path = ResourceDir + "/" + name;
AudioClip clip=Resources.Load(path);
m_EffectMusic.PlayOneShot(clip);
}
}
定义了格子和其中存储的能否放塔,和其中存储的数据
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//格子信息
public class Tile
{
public int X;
public int Y;
public bool CanHold; //是否可以放置塔
public object Data; //格子所保存的数据
public Tile(int x, int y)
{
this.X = x;
this.Y = y;
}
public override string ToString()
{
return string.Format("[X:{0},Y:{1},CanHold:{2}]",
this.X,
this.Y,
this.CanHold
);
}
}
包含了怪物的id和数量并提供了构造方法来设置数值
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
public class Round
{
public int Monster; //怪物类型ID
public int Count; //怪物数量
public Round(int monster, int count)
{
this.Monster = monster;
this.Count = count;
}
}
包含每个格子中心的x,y对,用作路径和可放塔点的存储
using UnityEngine;
using System.Collections;
//格子坐标
public class Point
{
public int X;
public int Y;
public Point(int x, int y)
{
this.X = x;
this.Y = y;
}
}
包含要读取的路径的图片,包含要读取的背景的图片名,level关卡的名字,可放塔点数组,路径数组,怪物波数组,初始的金币数
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class Level
{
//名字
public string Name;
//背景
public string Background;
//路径
public string Road;
//金币
public int InitScore;
//炮塔可放置的位置
public List Holder = new List();
//怪物行走的路径
public List Path = new List();
//出怪回合信息
public List Rounds = new List();
}
通过camera.viewporttoworldpoint,来通过摄像机的观察坐标获得对应的世界坐标,通过相减算出width和height,除以列数和行数来计算出对应的格子宽和格子高
void CalculateSize()
{
Vector3 leftDown = new Vector3(0, 0);
Vector3 rightUp = new Vector3(1, 1);
Vector3 p1 = Camera.main.ViewportToWorldPoint(leftDown);
Vector3 p2 = Camera.main.ViewportToWorldPoint(rightUp);
MapWidth = (p2.x - p1.x);
MapHeight = (p2.y - p1.y);
TileWidth = MapWidth / ColumnCount;
TileHeight = MapHeight / RowCount;
}
将格子的坐标remap到摄像机中心在世界中心原点的position上
Vector3 GetPosition(Tile t)
{
return new Vector3(
-MapWidth / 2 + (t.X + 0.5f) * TileWidth,
-MapHeight / 2 + (t.Y + 0.5f) * TileHeight,
0
);
}
Tile GetTile(int tileX, int tileY)
{
int index = tileX + tileY * ColumnCount;
if (index < 0 || index >= m_grid.Count)
return null;
return m_grid[index];
}
利用input.mouse并将其从screentoviewportpoint转换到view视图,再通过viewport转换到worldposition
Vector3 GetWorldPosition()
{
Vector3 viewPos = Camera.main.ScreenToViewportPoint(Input.mousePosition);
Vector3 worldPos = Camera.main.ViewportToWorldPoint(viewPos);
return worldPos;
}
用鼠标的x加上二分之一宽除以格子宽 再取int值舍去小数部分就可以得到鼠标所在的格子
用鼠标的y加上二分之一高除以格子高 再取int值舍去小数部分就可以得到鼠标所在的格子
Tile GetTileUnderMouse()
{
Vector2 wordPos = GetWorldPosition();
int col = (int)((wordPos.x + MapWidth / 2) / TileWidth);
int row = (int)((wordPos.y + MapHeight / 2) / TileHeight);
return GetTile(col, row);
}
提供一个画出辅助线的方法
在可以放置塔的格子放置加号图标
在初始路线放置起点图标
在路线尽头放置结束的图标
在中间的点位之间画上连线
void OnDrawGizmos()
{
// 提供一个画线的布尔值判断是否画线
if (!DrawGizmos)
return;
//计算地图和格子大小
CalculateSize();
Gizmos.color = Color.green;
//绘制格子
//绘制行
for (int row = 0; row <= RowCount; row++)
{
Vector2 from = new Vector2(-MapWidth / 2, -MapHeight / 2 + row * TileHeight);
Vector2 to = new Vector2(-MapWidth / 2 + MapWidth, -MapHeight / 2 + row * TileHeight);
Gizmos.DrawLine(from, to);
}
//绘制列
for (int col = 0; col <= ColumnCount; col++)
{
Vector2 from = new Vector2(-MapWidth / 2 + col * TileWidth, MapHeight / 2);
Vector2 to = new Vector2(-MapWidth / 2 + col * TileWidth, -MapHeight / 2);
Gizmos.DrawLine(from, to);
}
foreach (Tile t in m_grid)
{
if (t.CanHold)
{
Vector3 pos = GetPosition(t);
Gizmos.DrawIcon(pos, "holder.png", true);
}
}
Gizmos.color = Color.red;
for (int i = 0; i < m_road.Count; i++)
{
//起点
if (i == 0)
{
Gizmos.DrawIcon(GetPosition(m_road[i]), "start.png", true);
}
//终点
if (m_road.Count > 1 && i == m_road.Count - 1)
{
Gizmos.DrawIcon(GetPosition(m_road[i]), "end.png", true);
}
//红色的连线
if (m_road.Count > 1 && i != 0)
{
Vector3 from = GetPosition(m_road[i - 1]);
Vector3 to = GetPosition(m_road[i]);
Gizmos.DrawLine(from, to);
}
}
public static IEnumerator LoadImage(string url, Image image)
{
UnityWebRequest www = UnityWebRequestTexture.GetTexture(url);
yield return www.SendWebRequest();
if (www.isNetworkError || www.isHttpError)
{
Debug.Log(www.error);
}
else
{
Texture2D texture = DownloadHandlerTexture.GetContent(www);
Sprite sp = Sprite.Create(
texture,
new Rect(0, 0, texture.width, texture.height),
new Vector2(0.5f, 0.5f));
image.sprite = sp;
}
}
public string BackgroundImage
{
set
{
SpriteRenderer render = transform.Find("Background").GetComponent();
StartCoroutine(Tools.LoadImage(value, render));
}
}
public string RoadImage
{
set
{
SpriteRenderer render = transform.Find("Road").GetComponent();
StartCoroutine(Tools.LoadImage(value, render));
}
}
//加载图片
this.BackgroundImage = "file://" + Consts.MapDir + "/" + level.Background;
this.RoadImage = "file://" + Consts.MapDir + "/" + level.Road;
作为地图编辑器的图形接口显示在Inspector上面
加头文字*[CustomEditor(typeof(Map))]* 声明要对Map进行inspector修改