Unity-塔防游戏源码


本文提供全流程,中文翻译。

Chinar坚持将简单的生活方式,带给世人!

(拥有更好的阅读体验 —— 高分辨率用户请根据需求调整网页缩放比例)

1

有耐心的朋友可以跳转到SiKi学院,观看视频:SiKi学院

SiKi学院——是本人发现的网络教程做的很完善的网络课堂,推荐大家多学,多看

using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;
using UnityEngine.UI; /// <summary>
/// 建筑物控制脚本
/// </summary>
public class BuildManager : MonoBehaviour
{
public TurretData StandarTurretData; //标准炮塔数据
public TurretData MissileTurretData; //导弹炮塔数据
public TurretData LaserTurretData; //激光炮塔数据
private TurretData SelectedTurretData; //当前选择的炮塔(即将要建造的炮台)
private MapCube _selectedMapCube; //当前选择的游戏炮台
private int Money = 1000; //金币
public Text MoneyText; //金币文本框
public Animator MoneyAnimator;
public GameObject UpgradeCanvas;
public Button ButtonUpGrade;
private Animator UpgradeCanvasAnimator; void Start()
{
UpgradeCanvasAnimator = UpgradeCanvas.GetComponent<Animator>();
} void Update()
{
//容错,炮台的建造,需要满足2个条件
if (SelectedTurretData != null && Input.GetMouseButtonDown(0)) //如果鼠标右键按下;
{
if (EventSystem.current.IsPointerOverGameObject() == false) //检测鼠标在不在UI上。
{
//射线
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); //被照射物体
RaycastHit hit; //是否照射到物体 (射线,被照射物体,射线长度,MapCuber层的物体)
bool isCollider = Physics.Raycast(ray, out hit, 1000, LayerMask.GetMask("MapCube"));
if (isCollider)
{
MapCube mapcube = hit.collider.GetComponent<MapCube>(); //得到点击的mapcube //如果底板上 没有炮塔,则可以创建
if (mapcube.TurretGo == null)
{
//可以创建,然后判断钱够不够,
if (Money >= SelectedTurretData.Cost)
{
ChangeMoney(-SelectedTurretData.Cost); //减少钱
mapcube.BuildTurret(SelectedTurretData); //创建炮塔
}
else
{
//提示钱不够
MoneyAnimator.SetTrigger("Flicker");
}
}
else if (mapcube.TurretGo != null)
{
//选择的是否是同一个炮台&&UI升级框是否显示
if (mapcube.TurretGo == _selectedMapCube && UpgradeCanvas.activeInHierarchy)
{
StartCoroutine(HideUpGradeUi());
}
else
{
Vector3 upgraded = new Vector3(mapcube.transform.position.x, mapcube.transform.position.y + 2.6f, mapcube.transform.position.z);
//升级
ShowUpGradeUI(upgraded, mapcube.IsUpgraded);
} //选择的炮台,被赋值
_selectedMapCube = mapcube;
}
}
}
}
} public void OnStandarSelected(bool isOn)
{
if (isOn)
{
SelectedTurretData = StandarTurretData;
}
} public void OnMissileSelected(bool isOn)
{
if (isOn)
{
SelectedTurretData = MissileTurretData;
}
} public void OnLaserSelected(bool isOn)
{
if (isOn)
{
SelectedTurretData = LaserTurretData;
}
} /// <summary>
/// 金钱的变动
/// </summary>
/// <param name="change"></param>
private void ChangeMoney(int change = 0)
{
Money += change; //金币的变动
MoneyText.text = "¥" + Money; //金币文本框赋值
} /// <summary>
/// 显示升级按钮
/// </summary>
/// <param name="pos"></param>
/// <param name="isDisableUp"></param>
private void ShowUpGradeUI(Vector3 pos, bool isDisableUp = false) //是否禁用升级按钮
{
StopCoroutine(HideUpGradeUi());
UpgradeCanvas.SetActive(false); //显示按钮
UpgradeCanvas.transform.position = pos;
UpgradeCanvas.SetActive(true); //显示按钮
ButtonUpGrade.interactable = !isDisableUp; //是否可以交互/ false
} /// <summary>
/// 隐藏升级按钮
/// </summary>
IEnumerator HideUpGradeUi()
{
UpgradeCanvasAnimator.SetTrigger("Hide"); yield return new WaitForSeconds(0.6f); UpgradeCanvas.SetActive(false);
} /// <summary>
/// 点击升级按钮,升级炮塔
/// </summary>
public void OnUpGradeButtonDown()
{
if (Money >= _selectedMapCube._turretData.Cost) //钱够不够
{
ChangeMoney(-_selectedMapCube._turretData.Cost);
_selectedMapCube.UpGradeTurret();
}
else
{
MoneyAnimator.SetTrigger("Flicker");
} StartCoroutine(HideUpGradeUi());
} /// <summary>
/// 点击拆按钮。拆炮塔
/// </summary>
public void OnDestroyButtonDown()
{
_selectedMapCube.DestroyTurret();
StartCoroutine(HideUpGradeUi());
}
}

2

using UnityEngine;

/// <summary>
/// 子弹类脚本
/// </summary>
public class Bullet : MonoBehaviour
{
public int Damage = 50; //伤害
public float Speed = 20; //速度
private Transform Target; //目标 public GameObject ExplosionEffectPrefab; //子弹特效
private float DistanceArriveTarget = 2; //子弹于目标物体,向量长度 void Start()
{
} 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) //向量的长度magnitude
{
//子弹打到敌人,掉血
Target.GetComponent<Enemy>().TakeDamage(Damage); Die();
}
} private void Die()
{
//实例化特效
GameObject effect = Instantiate(ExplosionEffectPrefab, transform.position, transform.rotation) as GameObject; //删除特效
Destroy(effect, 1f); //销毁子弹
Destroy(this.gameObject);
} /// <summary>
/// 设置目标
///
/// </summary>
/// <param name="_target"></param>
public void SetTarget(Transform _target)
{
this.Target = _target;
}
}

3

using UnityEngine;
using System.Collections;
using UnityEngine.UI; /// <summary>
/// 敌人脚本
/// </summary>
public class Enemy : MonoBehaviour
{
//位置数组
private Transform[] positions; //下标,从0开始
private int index = 0; //速度
public float Speed = 10; public float Hp = 150; private float TotalHp; //总HP public GameObject ExplosionEffect; //敌人爆炸特效 private Slider HpSlider; void Start()
{
positions = WayPoints.positions; TotalHp = Hp; HpSlider = GetComponentInChildren<Slider>();
} void Update()
{
Move();
} void Move()
{
//下标越界,就跳出;
if (index > positions.Length - 1) return; //找一个方向,(目标位置-当前位置).normalized 取得这个向量的单位向量 ;
transform.Translate((positions[index].position - transform.position).normalized * Time.deltaTime * Speed); //判断 两点 之间的距离
if (Vector3.Distance(positions[index].position, transform.position) < 0.5f)
{
index++;
} if (index > positions.Length - 1)
{
ReachDestination();
}
} /// <summary>
/// ReachDestination 到达 目的地 销毁
/// </summary>
void ReachDestination()
{
Destroy(this.gameObject);
GameManager.instance.Failed();
} /// <summary>
/// 监听销毁敌人
/// </summary>
void OnDestroy()
{
EnemySpawner.CountEnemyAlive--;
} /// <summary>
/// 敌人受到伤害
/// </summary>
/// <param name="damage"></param>
public void TakeDamage(float damage)
{
if (Hp <= 0) return; //死了不处理
Hp -= damage; //减少血量
HpSlider.value = (float) Hp / TotalHp; //血条
if (Hp <= 0)
{
Die();
}
} private void Die()
{
GameObject effect = Instantiate(ExplosionEffect, transform.position, transform.rotation) as GameObject; Destroy(effect, 1.5f); Destroy(this.gameObject);
}
}

4

using UnityEngine;
using System.Collections; /// <summary>
/// 敌人生成控制脚本
/// </summary>
public class EnemySpawner : MonoBehaviour
{
//属性面板
public Wave[] Waves; //生成位置
public Transform StartTran; //孵化间隔
public float WaveRate = 0.2f; //存活敌人的数量
public static int CountEnemyAlive = 0; private Coroutine coroutine; void Start()
{
coroutine = StartCoroutine(SpawnEnemy());
} /// <summary>
/// 敌人孵化器
/// </summary>
/// <returns></returns>
IEnumerator SpawnEnemy()
{
//遍历所有Waves中的 数组
foreach (Wave wave in Waves)
{
for (int i = 0; i < wave.Count; i++)
{
//实例化预设物(每一波敌人的预设物,开始位置,无旋转的)旋转前的初始角
GameObject.Instantiate(wave.EnemyPrefab, StartTran.position, Quaternion.identity); CountEnemyAlive++; //存活数量增加 if (i != wave.Count - 1) //检测生成是否完毕
{
//时间间隔
yield return new WaitForSeconds(wave.Rate);
}
} //当存活数量大于0
while (CountEnemyAlive > 0)
{
yield return 0; //暂停0帧,不执行下边代码
} //每一波暂停 WaveRate 秒
yield return new WaitForSeconds(WaveRate);
}
} /// <summary>
/// 停止生成
/// </summary>
public void Stop()
{
StopCoroutine(coroutine);
}
}

5

using UnityEngine;
using UnityEngine.EventSystems; /// <summary>
/// 地图版块控制脚本
/// </summary>
public class MapCube : MonoBehaviour
{
[HideInInspector] //不显示在 Inspector 面板
public GameObject TurretGo; //保存当前Cube身上的炮台
[HideInInspector] public TurretData _turretData;
public GameObject BuildEffect; private Renderer CubeRenderer; [HideInInspector] public bool IsUpgraded; //MapCub上的炮塔是否升级过 void Start()
{
CubeRenderer = GetComponent<Renderer>();
} /// <summary>
/// 点击创建炮塔
/// </summary>
/// <param name="turretPrefab"></param>
public void BuildTurret(TurretData turretData)
{
this._turretData = turretData;
IsUpgraded = false;
//创建炮塔,位置,旋转,强转
TurretGo = Instantiate(turretData.TurretPrefab, transform.position, Quaternion.identity) as GameObject;
GameObject effectobj = Instantiate(BuildEffect, transform.position, Quaternion.identity) as GameObject;
Destroy(effectobj, 1f);
} /// <summary>
/// 升级炮台
/// </summary>
/// <param name="turretgo"></param>
public void UpGradeTurret()
{
if (IsUpgraded == true) return; //容错
Destroy(TurretGo); //删除低级炮塔
IsUpgraded = true; //升级状态变化
//创建炮塔,位置,旋转,强转
TurretGo = Instantiate(_turretData.TurretUpGradedPrefab, transform.position, Quaternion.identity) as GameObject;
GameObject effectobj = Instantiate(BuildEffect, transform.position, Quaternion.identity) as GameObject;
} /// <summary>
/// 拆除炮台
/// </summary>
public void DestroyTurret()
{
Destroy(TurretGo);
GameObject effectobj = Instantiate(BuildEffect, transform.position, Quaternion.identity) as GameObject;
IsUpgraded = false; //升级状态变化
TurretGo = null;
_turretData = null;
} /// <summary>
/// 鼠标移入
/// </summary>
private void OnMouseEnter()
{
//Cube上没有炮台,且,鼠标没有放在UI上
if (TurretGo == null && EventSystem.current.IsPointerOverGameObject() == false)
{
CubeRenderer.material.color = Color.red; //变红
}
} /// <summary>
/// 鼠标移出
/// </summary>
private void OnMouseExit()
{
CubeRenderer.material.color = Color.white; //变红
}
}

6

using UnityEngine;

/// <summary>
/// 炮塔模型类
/// </summary>
[System.Serializable] //可序列化
public class TurretData
{
//炮塔预设物
public GameObject TurretPrefab; //花费
public int Cost; //炮塔升级预设物
public GameObject TurretUpGradedPrefab; //升级炮塔花费
public int CostUpGraded; //默认枚举类型
public TurretType Type;
} /// <summary>
/// 枚举一个炮塔类
/// </summary>
public enum TurretType
{
StandardTurret, //标准
MissileTurret, //导弹
LaserTurret //激光
}

7

using UnityEngine;
using System.Collections.Generic; /// <summary>
/// 炮塔控制脚本类
/// </summary>
public class Turret : MonoBehaviour
{
private List<GameObject> Enemys = new List<GameObject>(); //进入方位的敌人
public float AttackRateTime = 1; //攻击间隔
private float Timer = 0; //计时器
public GameObject BulletPrefab; //子弹 预设物
public Transform FirePos;
public Transform Head;
public bool UseLaser = false; //默认攻击方式
public float DamageRate = 70; //激光炮塔,持续伤害 速率。每秒70点伤害
public LineRenderer LaseRenderer;
public GameObject LaserEffect; //激光特效 void Start()
{
Timer = AttackRateTime;
} void Update()
{
if (Enemys.Count > 0 && Enemys[0] != null)
{
Vector3 targetPosition = Enemys[0].transform.position;
targetPosition.y = Head.position.y;
Head.LookAt(targetPosition);
} if (UseLaser == false)
{
Timer += Time.deltaTime; //敌人数量大于0,且满足间隔时间
if (Enemys.Count > 0 && Timer >= AttackRateTime)
{
Timer = 0;
Attack();
}
} else if (Enemys.Count > 0)
{
if (LaseRenderer.enabled == false) LaseRenderer.enabled = true; if (Enemys[0] == null)
{
UpDateEnemys();
} if (Enemys.Count > 0)
{
//激光炮塔朝向
LaseRenderer.SetPositions(new Vector3[] {FirePos.position, Enemys[0].transform.position}); //激光炮塔攻击
Enemys[0].GetComponent<Enemy>().TakeDamage(DamageRate * Time.deltaTime);
LaserEffect.SetActive(true);
LaserEffect.transform.position = Enemys[0].transform.position; //激光特效,朝向炮塔
Vector3 pos = transform.position;
pos.y = Enemys[0].transform.position.y;
LaserEffect.transform.LookAt(pos);
}
}
else
{
LaserEffect.SetActive(false);
LaseRenderer.enabled = false;
}
} /// <summary>
/// 攻击方法
/// </summary>
private void Attack()
{
if (Enemys[0] == null)
{
UpDateEnemys();
} if (Enemys.Count > 0)
{
//实例化子弹
GameObject bullet = Instantiate(BulletPrefab, FirePos.position, FirePos.rotation) as GameObject; //子弹 指向目标
bullet.GetComponent<Bullet>().SetTarget(Enemys[0].transform);
}
else
{
Timer = AttackRateTime; //下次来敌人,直接攻击
}
} /// <summary>
/// 进入区域
/// </summary>
/// <param name="col"></param>
private void OnTriggerEnter(Collider col)
{
if (col.tag == "Enemy")
{
Enemys.Add(col.gameObject);
}
} /// <summary>
/// 离开区域
/// </summary>
/// <param name="col"></param>
private void OnTriggerExit(Collider col)
{
if (col.tag == "Enemy")
{
Enemys.Remove(col.gameObject);
}
} /// <summary>
/// 更新集合
/// </summary>
private void UpDateEnemys()
{
//用来存空元素下标 的数组
List<int> EmptyIndex = new List<int>(); //移除所有为空的元素,遍历进入范围的敌人数组长度。
for (int i = 0; i < Enemys.Count; i++)
{
if (Enemys[i] == null)
{
EmptyIndex.Add(i);
}
} //移除 所有空元素
for (int i = 0; i < EmptyIndex.Count; i++)
{
//由于Enemys移除元素,下标会变动。所以减去,移除的个数;
Enemys.RemoveAt(EmptyIndex[i] - i);
}
}
}

8

using UnityEngine;
using System.Collections; /// <summary>
/// 玩家视角控制脚本
/// </summary>
public class ViewController : MonoBehaviour
{
public float Speed = 1; //速度
public float MouseSpeed = 249; //速度 void Start()
{
} void Update()
{
//获取水平和垂直参数
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
float mouse = Input.GetAxis("Mouse ScrollWheel") * MouseSpeed;
//控制移动,并根据时间平滑移动,根据世界坐标移动
transform.Translate(new Vector3(h * Speed, mouse, v * Speed) * Time.deltaTime, Space.World);
}
}

9

using UnityEngine;

/// <summary>
/// 保存每一波敌人生成所需要的属性
/// </summary>
[System.Serializable] //可以被序列化的属性
public class Wave
{
public GameObject EnemyPrefab; //敌人
public int Count; //数量
public float Rate; //时间间隔
}

10

有耐心的朋友可以跳转到SiKi学院,观看视频:SiKi学院

using UnityEngine;

/// <summary>
/// 路径导航脚本
/// </summary>
public class WayPoints : MonoBehaviour
{
//拐点,数组
public static Transform[] positions; void Awake()
{
//找到当前,挂载脚本物体下的,子物体数量
positions = new Transform[transform.childCount]; //依次遍历所有子物体,得到依次的位置
for (int i = 0; i < positions.Length; i++)
{
positions[i] = transform.GetChild(i);
}
}
}

1 —— 云服务器超全购买流程 (新手必备!)



2 —— 阿里ECS云服务器自定义配置 - 购买教程(新手必备!)



3—— Windows 服务器配置、运行、建站一条龙 !



4 —— Linux 服务器配置、运行、建站一条龙 !



" role="presentation">

技术交流群:806091680 ! Chinar 欢迎你的加入


END

本博客为非营利性个人原创,除部分有明确署名的作品外,所刊登的所有作品的著作权均为本人所拥有,本人保留所有法定权利。违者必究


对于需要复制、转载、链接和传播博客文章或内容的,请及时和本博主进行联系,留言,Email: ichinar@icloud.com


对于经本博主明确授权和许可使用文章及内容的,使用时请注明文章或内容出处并注明网址>

Unity 3D游戏-塔防类游戏源码:重要方法和功能的实现的更多相关文章

  1. unity3D游戏开发实战原创视频讲座系列9之塔防类游戏开发第一季

    解说文件夹 塔防游戏0基础篇... 第一讲  游戏演示和资源介绍... 第二讲  游戏场景的完毕... 第三讲  预制体的制作... 第四讲  敌人的随机产生和按路径行走... 第五讲  塔防工具的产 ...

  2. 一款类似塔防类的保卫羊村游戏android源码

    一款类似塔防类的保卫羊村游戏源码,这个游戏很像我们平时玩的塔防游戏的,游戏的源码比较完整的,大家可以修改一下或者添加一些广告就可以上线到应用商店了,而且这个游戏目前已经上线国内的一些应用商店了,360 ...

  3. 3D单机游戏《天鹰教》源码发布(二)

    3D单机游戏<天鹰教>源码发布 作者 作者: 游蓝海 博客: http://blog.csdn.net/you_lan_hai mail:     you_lan_hai@foxmail. ...

  4. 25 BasicUsageEnvironment0基本使用环境基类——Live555源码阅读(三)UsageEnvironment

    25 BasicUsageEnvironment0基本使用环境基类——Live555源码阅读(三)UsageEnvironment 25 BasicUsageEnvironment0基本使用环境基类— ...

  5. 24 UsageEnvironment使用环境抽象基类——Live555源码阅读(三)UsageEnvironment

    24 UsageEnvironment使用环境抽象基类——Live555源码阅读(三)UsageEnvironment 24 UsageEnvironment使用环境抽象基类——Live555源码阅读 ...

  6. 12 哈希表相关类——Live555源码阅读(一)基本组件类

    12 哈希表相关类--Live555源码阅读(一)基本组件类 这是Live555源码阅读的第一部分,包括了时间类,延时队列类,处理程序描述类,哈希表类这四个大类. 本文由乌合之众 lym瞎编,欢迎转载 ...

  7. String类的源码分析

    之前面试的时候被问到有没有看过String类的源码,楼主当时就慌了,回来赶紧补一课. 1.构造器(构造方法) String类提供了很多不同的构造器,分别对应了不同的字符串初始化方法,此处从源码中摘录如 ...

  8. IDEA定位到类的代码区域(查看类的源码)

    经常需要查看某一个类中的成员变量和方法,那么怎么进入到这个类的源码区域呢?在IDEA中只需要使用快捷键: ctrl+shift+t 就可以快速定位到这个类的源码.

  9. 分享自己用的php分页类实例源码

    分享一个我自己用着的php分页类实例源码,供大家参考,具体内容如下: <?php /** file: page.class.php 完美分页类 Page */ class Page { priv ...

随机推荐

  1. JavaMai——邮箱验证用户注册

    这篇文章简单的模拟了网上利用邮箱激活用户注册这样的一个功能 1. 呈现给用户的注册界面:(为了简单起见,就剩下两个输入域,邮箱和昵称) <%@ page language="java& ...

  2. Qt_Android_书

    1. http://bbs.csdn.net/topics/390942701 <<Qt on Android 核心编程>> Qt Quick核心编程 2.

  3. [原][osg][osgearth]简单的通过osgDB,读取高程tif,修改高程tif

    ReadResult result; osg::ref_ptr<osgDB::ReaderWriter> reader = osgDB::Registry::instance()-> ...

  4. 《剑指offer》第二十题(表示数值的字符串)

    // 面试题20:表示数值的字符串 // 题目:请实现一个函数用来判断字符串是否表示数值(包括整数和小数).例如, // 字符串“+100”.“5e2”.“-123”.“3.1416”及“-1E-16 ...

  5. 【Python】Http Post请求四种请求体的Python实现

    前言 前几天一个刚接触Python不深的朋友问我的Python的xml格式Post请求怎么发送,刚好最近也在学习Http请求相关的内容,所以决定总结一下各类Post请求的Python实现. Happy ...

  6. Illumina Sequence Identifiers 序列标识符 index详解

    大家基本都知道什么是 FASTA 和 FastQ 格式了,但这是不够的. 我们还需要了解世界上最大的测序公司自己定制的 FastQ 格式,因为你可能会经常用到,有时还会亲自去处理它们. 本文主题:Il ...

  7. Jersey 2.x 探索新建的工程

    如果用 Jersey maven archetype 成功创建了这个项目,那么在你当前的路径下就已经创建了一个名为simple-service项目.它包含了一个标准的Maven项目结构: 说明 文件目 ...

  8. Sasha and Interesting Fact from Graph Theory CodeForces - 1109D (图论,计数,Caylay定理)

    大意: 求a->b最短路长度为m的n节点树的个数, 边权全部不超过m 枚举$a$与$b$之间的边数, 再由拓展$Caylay$定理分配其余结点 拓展$Caylay$定理 $n$个有标号节点生成k ...

  9. Docker+Hadoop+Hive+Presto 使用Docker部署Hadoop环境和Presto

    Background 一. 什么是Presto Presto通过使用分布式查询,可以快速高效的完成海量数据的查询.如果你需要处理TB或者PB级别的数据,那么你可能更希望借助于Hadoop和HDFS来完 ...

  10. OAF 获取页面路径

    --模糊查询某个页面 SELECT * FROM JDR_PATHS jp WHERE JP.PATH_NAME LIKE '%XXXX%'; --精确的查找过程 -- DocID --参数通过关于此 ...