文章目录

  • 框架的高层设计
  • 新建项目与项目结构
  • 编写框架
    • 对象池
      • IReusable interface
        • 知识补充:abstract和virtual的区别
        • 代码实现
      • UseableObjects
        • 代码实现
      • Subpool
        • Gameobject.instantiate
        • 代码实现
      • ObjectPool
        • 代码实现
    • Mono的单例模式基类
      • 代码实现
    • 音乐音效播放模块
      • 代码实现
    • MVC框架
      • MVC原理
  • 新建场景编辑器
    • 声明各个类
      • Tile格子类
      • Round怪物波数类
      • Point各个格子的中心点类
      • level关卡类
    • Map类实现各个功能
      • 计算地图大小,格子大小
      • 得到格子中心点的世界坐标
      • 根据格子的行和列还得到对应的格子
      • 获取鼠标所在的世界位置
      • 计算鼠标所在的格子
      • 在编辑器中执行的OnDrawGizmos
      • UnityWetRequest加载图片
      • 修改spriterender上的sprite
    • 地图编辑器MapEditor

前段时间笔记都写在平板上了哈哈 后面发现还是电脑方便好用 以后还是写在电脑上吧

框架的高层设计

包含策划设计的应用层明显需求,程序需要自己设计框架性需求

新建项目与项目结构


建立如图所示的项目结构,将其中的导出设置依次设置其中四个scenes

编写框架

对象池


IReusable interface

作为ReusableObject的上层接口供给ReusableObject挂在Gameobject上面

知识补充:abstract和virtual的区别

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();
}

UseableObjects

代码实现

/** @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: liuletonghenshuai@163.com* */
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public abstract class Reusableobject : MonoBehaviour,IReusable
{//定义抽象类和抽象方法,子类必须对其进行重写public abstract void OnSpawn();public abstract void OnUnSpawn();
}

Subpool

作为各个物体的子池,提供了Spawn,UnSpawn和UnSpawnAll方法

Gameobject.instantiate

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: liuletonghenshuai@163.com* */
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.Text;
public class Subpool
{//声明prefab以及集合作为对象池GameObject m_prefab;List<GameObject> m_objects = new List<GameObject>();//构造函数为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<GameObject>(m_prefab);m_objects.Add(go);              }go.SetActive(true);go.SendMessage("OnSpawn",SendMessageOptions.DontRequireReceiver);return go;}//移除子池中一个gameobjectpublic void Unspawn(GameObject go){if(m_objects.Contains(go)){go.SetActive(false);go.SendMessage("OnUnSpawn",SendMessageOptions.DontRequireReceiver);}}//移除子池中所有的gameobjectpublic void UnspawnAll(){foreach (GameObject item in m_objects){if(item.activeSelf){Unspawn(item);}}}//供给上层使用验证是否包含gameobjpublic bool Contains(GameObject go ){return m_objects.Contains(go);}
}

ObjectPool

作为总池来管理子池,统筹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: liuletonghenshuai@163.com* */using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class ObjectPool : MonoBehaviour
{//字典存储子池和他对应的名字Dictionary<string,Subpool> m_pools = new Dictionary<string,Subpool>();//资源相对于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;elsepath = ResourceDir + "/" + name;GameObject prefab = Resources.Load<GameObject>(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);}//直接逐个释放每个subpoolpublic void UnspawnAll(){foreach (Subpool item in m_pools.Values){item.UnspawnAll();}}
}

Mono的单例模式基类

之前框架的里面都写过了不明白了回去看这里就不解释了

代码实现

using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class SingletonMono <T> : 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: liuletonghenshuai@163.com* */
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class MusicMgr : SingletonMono<MusicMgr>
{//分开播放背景音乐和效果音乐public string ResourceDir ="";public AudioSource m_BgMusic;public AudioSource m_EffectMusic;protected override void Awake() {base.Awake();m_BgMusic = this.gameObject.AddComponent<AudioSource>();m_BgMusic.loop=true;m_BgMusic.playOnAwake=false;m_EffectMusic = this.gameObject.AddComponent<AudioSource>();}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="";elseoldName =m_BgMusic.clip.name;if(oldName!=name) {//先令path为空string path = null;//判断相对路径是否为空,设定目录位置if(string.IsNullOrEmpty(ResourceDir))path = name;elsepath = ResourceDir + "/" + name;AudioClip clip=Resources.Load<AudioClip>(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;elsepath = ResourceDir + "/" + name;AudioClip clip=Resources.Load<AudioClip>(path);m_EffectMusic.PlayOneShot(clip);}}

MVC框架

MVC原理

新建场景编辑器

声明各个类

Tile格子类

定义了格子和其中存储的能否放塔,和其中存储的数据

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);}
}

Round怪物波数类

包含了怪物的id和数量并提供了构造方法来设置数值

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;public class Round
{public int Monster; //怪物类型IDpublic int Count;   //怪物数量public Round(int monster, int count){this.Monster = monster;this.Count = count;}
}

Point各个格子的中心点类

包含每个格子中心的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关卡类

包含要读取的路径的图片,包含要读取的背景的图片名,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<Point> Holder = new List<Point>();//怪物行走的路径public List<Point> Path = new List<Point>();//出怪回合信息public List<Round> Rounds = new List<Round>();
}

Map类实现各个功能

计算地图大小,格子大小

通过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);}

在编辑器中执行的OnDrawGizmos

提供一个画出辅助线的方法
在可以放置塔的格子放置加号图标
在初始路线放置起点图标
在路线尽头放置结束的图标
在中间的点位之间画上连线

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);}}

UnityWetRequest加载图片

    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;}}

修改spriterender上的sprite

    public string BackgroundImage{set{SpriteRenderer render = transform.Find("Background").GetComponent<SpriteRenderer>();StartCoroutine(Tools.LoadImage(value, render));}}public string RoadImage{set{SpriteRenderer render = transform.Find("Road").GetComponent<SpriteRenderer>();StartCoroutine(Tools.LoadImage(value, render));}}//加载图片this.BackgroundImage = "file://" + Consts.MapDir + "/" + level.Background;this.RoadImage = "file://" + Consts.MapDir + "/" + level.Road;

地图编辑器MapEditor

作为地图编辑器的图形接口显示在Inspector上面
加头文字*[CustomEditor(typeof(Map))]* 声明要对Map进行inspector修改

仿保卫萝卜Unity塔防游戏开发相关推荐

  1. unity塔防游戏开发之基于标点法来简易实现敌人路线移动

    unity塔防游戏开发之基于标点法来简易实现敌人路线移动 首先创建一个物体,把它设置为静态物体,给他创建一系列子物体,将这些子物体放在各个路线的转角处,这样就实现标点的目的,然后将这些路标的位置都设置 ...

  2. Unity塔防游戏的制作与实现

    一.游戏场景的搭建 首先,我们需要创建一个新的Unity场景,并将场景设置为2D模式.然后,我们需要导入一些必要的素材,如地图.塔.怪物.子弹等.我们可以从Unity Asset Store中下载这些 ...

  3. unity塔防游戏,可以正常运行

    unity塔防游戏 游戏截图如下:(下载链接在文末) 点我下载资源 https://download.csdn.net/download/weixin_43474701/64828307

  4. unity塔防游戏怪物转向_Unity官方新手游戏项目推荐合集

    Unity官方新手游戏项目推荐合集 今天给同学们介绍一些Unity官方发布过的一些游戏项目,这些项目都简化了游戏开发的入门学习过程,可以快速地制作出游戏,适合新手入门体验,下面就带同学们看一看: Un ...

  5. unity塔防游戏怪物转向_玩一玩这款塔防游戏?

    关注上方蓝字获得更多内容 今日分享塔防游戏 塔防游戏,曾经也是风靡一时,非常受人喜欢的. 今天小皮分享的这款和保卫萝卜可以说是异曲同工,还是不错的. 该游戏拥有精美的画面,并且在形象设计上也制作的相当 ...

  6. unity塔防游戏怪物转向_浅谈塔防游戏的“进化史”,独特创意把“策略至上”推到新的高度...

    塔防游戏的起源是一款叫<堡垒>的街机游戏,虽然说该游戏和目前的塔防游戏还有一些区别,但是目前的塔防游戏基本上都有借鉴这个游戏的内容,说它是塔防游戏的鼻祖也不为过.真正把塔防游戏带到大家面前 ...

  7. unity塔防游戏怪物转向_英雄塔防物语游戏下载-英雄塔防物语官方版 v2.3.8最新版...

    英雄塔防物语手游欢迎大家来下载,英雄塔防物语是一款二次元塔防放置手游,这款游戏为大家带来全新的角色,玩家可以收集卡牌进行组合对战,玩法十分丰富,还有丰富的剧情等你来解锁哦. [游戏简介] 英雄塔防物语 ...

  8. unity塔防游戏怪物转向_野生防御塔游戏下载-野生防御塔游戏安卓版 v1.0

    野生防御塔是一款塔防策略类型的游戏,经典好玩的塔防策略玩法等你来,在这开启一场刺激的塔防吧.你需要依靠塔防建筑抵挡怪物的攻击,同时也需要面对对手的偷袭.依靠击杀怪物获得的金币可以购买一些道具升级你的塔 ...

  9. unity塔防游戏怪物转向_一款塔防+第一人称射击的混合类游戏

    ​HELLO-大家好,这里是小白的每日一游推荐时间.世上的游戏千千万,有许多好玩的游戏由于缺乏宣传,所以不被广大玩家所熟知.在这里小白每天会为大家推荐一款评价很高但是不太出名的游戏- <幽闭圣地 ...

最新文章

  1. 如何解决visual studio2017 install 下载安装极慢的问题
  2. 自动驾驶进入第3阶段 ADAS黑科技守护开车人
  3. python进行图片的定位切割_python3 实现对图片进行局部切割的方法
  4. catboost原理以及Python代码
  5. 【项目实战】mybatis +vue.js 前后端交互批量删除
  6. android软件获取系统签名
  7. 杭电2100Lovekey
  8. 技术就是力量?!有人拿走百万现金,真相却是......
  9. ajax的content-download时间过慢问题的解决与思考
  10. CriminalIntent项目开发
  11. 吉他录音混音教程|连这些录音知识都不懂,以后还怎么“混”?| MZD Studios
  12. 在线画板_在线画画_在线画图工具-速写板
  13. 【推荐】HDC2021华为开发者大会技术分论坛内部课件合集
  14. 获取上个月第一天和最后一天
  15. 未来IT互联网企业的发展前景
  16. 2018 东北四省赛
  17. HTML required
  18. 连分数与丢番图方程简介
  19. 表达式的LenB(123程序设计ABC)的值是27吗
  20. 操作系统(存储管理)

热门文章

  1. html中如何写系统时间,在HTML页面获取当前系统时间
  2. Vim正则表达式匹配替换字符串
  3. R CrossTable生成二维列联表
  4. 程序员跳槽时,如何选择公司
  5. python中可选参数_正确使用Python可选参数
  6. 厉害了,二本大学生这样拿下阿里 Offer!| 程序员有话说
  7. 2008系统开ftp服务器配置,win2008 r2开启ftp服务器配置
  8. 见或不见! see me or not!
  9. Promise相关学习
  10. amber教程A17学习----概念篇