1、游戏管理器

游戏管理器负责管理游戏的整体流程,还可以系统管理用于游戏的全局数据以及游戏中判断胜败的条件。
游戏管理器并不是单一的模块,更像是能控制游戏的功能集合。
1)怪兽出现逻辑:专门设置一些位置用于随机生成怪兽。

public Transform[] points;
points = GameObject.Find("MonsterPos").GetComponentsInChildren<Transform>();

2)访问游戏管理器:声明GameManager类的变量,在Start函数中分配为局部变量。

private GameManager gameMgr;
gameMgr = GameObject.Find("GameManager").GetComponent<GameManager>();

3)单例模式:该模式下只有一个对象,可以获得全局访问。

public static GameManager instance = null;
void Awake()
{
//将GameManager带入实例
instance = this;
}

4)对象池:对于需要在游戏中反复生成的模型,可以在游戏一开始运行并加载场景时就全部生成,运行时可从中直接取出使用。

void Start ()
{
//生成怪兽并保存到对象池
for(int i = ;i<maxMonster;i++)
{
GameObject monster = (GameObject)Instantiate(monsterPrefab);
monster.name = "Monster_" + i.ToString();
monster.SetActive(false);
monsterList.Add(monster);
}
}

5)共享函数:声音处理

//声音共享函数
public void PlaySfx(Vector3 pos,AudioClip sfx)
{
//如果静音选项为true,则立即停止声音
if (isSfxMute) return;
//动态生成游戏对象
GameObject soundObj = new GameObject("Sfx");
soundObj.transform.position = pos;
//向生成的游戏对象添加AudioSource组件
AudioSource audioSource = soundObj.AddComponent<AudioSource>();
//设置AudioSource属性
audioSource.clip = sfx;
audioSource.minDistance = ;
audioSource.maxDistance = ;
audioSource.volume = sfxVolumn;
audioSource.Play();
//声音结束播放后,删除之前动态生成的游戏对象
Destroy(soundObj, sfx.length);
}

2、射线投射

射线投射不但常用于发射逻辑,还可用于游戏中的传感器以及通过鼠标移动、旋转玩家角色。

射线无法用肉眼观察,可以使用Debug.DrawRay()函数使射线在场景视图中显示。

void Update()
{
//使用以下函数在场景中显示射线
Debug.DrawRay(FirePos.position,FirePos.forward * , Color.green);
}

使怪兽被射线击中时受伤

            RaycastHit hit;
if (Physics.Raycast(FirePos.position, FirePos.forward, out hit, ))
{
if (hit.collider.tag == "Monster")
{
//SendMessage函数要传递的参数数组
object[] _params = new object[];
_params[] = hit.point;//被射线击中的位置
_params[] = ;//怪兽受到的伤害值 hit.collider.gameObject.SendMessage("OnDamage", _params, SendMessageOptions.DontRequireReceiver);
}
}

激光束

使用Line Renderer组件,因为Line Renderer将在Player的中心位置生成,所以不需要勾选Use World Space选项。

using UnityEngine;
using System.Collections; public class LaserBeam : MonoBehaviour
{
private Transform tr;
private LineRenderer line;
private RaycastHit hit; void Start ()
{
tr = GetComponent<Transform>();
line = GetComponent<LineRenderer>();
line.useWorldSpace = false;
line.enabled = false;
//设置头部宽度和尾部宽度
line.SetWidth(0.3f,0.01f);
} void Update ()
{
Ray ray = new Ray(tr.position + (Vector3.up * 0.02f),tr.forward);
Debug.DrawRay(ray.origin, ray.direction * , Color.blue);
if (Input.GetMouseButtonDown())
{
//设置Line Renderer的初始位置
line.SetPosition(,tr.InverseTransformPoint(ray.origin));
//(局部坐标基准Transform组件).InverseTransformPoint(全局坐标)
//将物体被射线击中的位置设置为Line Renderer的终点位置
if (Physics.Raycast(ray,out hit,))
{
line.SetPosition(,tr.InverseTransformPoint(hit.point));
}
else
line.SetPosition(, tr.InverseTransformPoint(ray.GetPoint())); StartCoroutine(ShowLaserBeam());
}
} IEnumerator ShowLaserBeam()
{
line.enabled = true;
yield return new WaitForSeconds(Random.Range(0.01f,0.2f));
line.enabled = false;
}
}

3、制作Player

1)玩家移动

using UnityEngine;
using System.Collections; [System.Serializable]
public class Anim
{
public AnimationClip idle;
public AnimationClip runForward;
public AnimationClip runBackward;
public AnimationClip runRight;
public AnimationClip runLeft;
}
public class PlayerCtrl : MonoBehaviour
{
private Transform tr;
public float moveSpeed = ;
public float rotSpeed = ;
public Anim anim;//要显示到检视视图的动画变量
public Animation _animation;//Animation组件对象的变量
public int hp = ; //声明委托和事件
public delegate void PlayerDieHandler();
public static event PlayerDieHandler OnPlayerDie; //Player的生命初始值
private int initHp;
//Player的生命图像
public Image imgHpBar;
public Text txtHp; //private GameMgr gameMgr; void Start ()
{
//设置生命值
initHp = hp;
tr = GetComponent<Transform>();
//查找位于自身下级的Animation组件并分配到变量
_animation = GetComponentInChildren<Animation>();
//保存并运行Animation组件的动画片段
_animation.clip = anim.idle;
_animation.Play(); //gameMgr = GameObject.Find("GameManager").GetComponent<GameMgr>();
} void Update ()
{
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical"); Vector3 dir = h * Vector3.right + v * Vector3.forward;//前后左右移动方向向量
tr.Translate(dir.normalized * moveSpeed * Time.deltaTime,Space.Self);//向量方向*速度*时间,以局部坐标为基准移动
tr.Rotate(Vector3.up * rotSpeed * Time.deltaTime * Input.GetAxis("Mouse X")); //以键盘输入值为基准,执行要操作的动画
//动画合成:让动画片段之间平滑过渡,用到CrossFade合成函数
if (v >= 0.1f)
{
_animation.CrossFade(anim.runForward.name, 0.3f);
}
else if (v <= -0.1f)
{
_animation.CrossFade(anim.runBackward.name, 0.3f);
}
else if (h <= -0.1f)
{
_animation.CrossFade(anim.runLeft.name, 0.3f);
}
else if (h >= 0.1f)
{
_animation.CrossFade(anim.runRight.name, 0.3f);
}
else
{
_animation.CrossFade(anim.idle.name, 0.3f);
}
} void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "Punch")
{
hp -= ;
//调整Image的fillAmount属性,以调整生命条长度
float xueliang = (float)hp / (float)initHp;
imgHpBar.fillAmount = xueliang;
txtHp.text = "<color=#ffffff>" + (xueliang * ).ToString() + "%</color>";
Debug.Log("Player HP =" + hp.ToString()); if (hp <= )
{
PlayerDie();
}
}
} void PlayerDie()
{
Debug.Log("Player Die!!"); //GameObject[] monsters = GameObject.FindGameObjectsWithTag("Monster");
//foreach (GameObject monster in monsters)
//{
// monster.SendMessage("OnPlayerDie",SendMessageOptions.DontRequireReceiver);
//}
//或者
//触发事件
OnPlayerDie(); //更新游戏管理器的isGameover变量以停止生成怪兽
//gameMgr.isGameOver = true;
GameMgr.instance.isGameOver = true;
} }

使用CharacterController组件后代码

float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical"); movDir = (tr.forward * v) + (tr.right * h);//计算移动方向
movDir.y -= 20 * Time.deltaTime;//更改y值使其受重力影响下落
tr.Rotate(Vector3.up * rotSpeed * Time.deltaTime * Input.GetAxis("Mouse X"));
controller.Move(movDir * moveSpeed * Time.deltaTime);//移动玩家

2)摄像机追踪

将该脚本添加到主摄像机下,并把Player拖拽到targetTr变量。

using UnityEngine;
using System.Collections; public class FollowCam : MonoBehaviour
{
public Transform tr;
public Transform targetTr;//要追踪的游戏对象的Trans变量
public float dist = ;//与摄像机之间的距离
public float height = ;//设置摄像机高度
public float dampTrace = ;//实现平滑追踪的变量 void Start ()
{
tr = GetComponent<Transform>();
} //要追踪的目标游戏对象停止移动后,调用LaterUpdate函数
void LateUpdate ()
{
//将摄像机放置在被追踪的目标后方的dist距离的位置
//将摄像机向上抬离height
tr.position = Vector3.Lerp(tr.position, //起始位置
targetTr.position - (targetTr.forward * dist) + (Vector3.up * height), //终止位置
Time.deltaTime * dampTrace); //内插时间
//使摄像机朝向游戏对象
tr.LookAt(targetTr.position);
}
}

4、网络管理器

Network View组件提供与其他玩家通信的功能,必须已经在网络上生成游戏或已访问游戏,所以其前提是游戏房间已经生成或已连接其他游戏客户端。

1)通过GUI类实现画面输入所需UI及制作网络玩家

using UnityEngine;
using System.Collections; public class NetworkManager : MonoBehaviour
{
//连接IP
private const string ip = "127.0.0.1";
//连接Port端口号
private const int port = ;
//是否使用NAT功能
private bool _useNat = false; public GameObject player; void OnGUI()
{
//判断当前玩家是否连接网络
if (Network.peerType == NetworkPeerType.Disconnected)
{
//生成启动游戏服务器按钮
if (GUI.Button(new Rect(,,,),"Start Server"))
{
//初始化游戏服务器
Network.InitializeServer(,port,_useNat);//连接数,端口号,是否用NAT
}
//生成连接游戏的按钮
if (GUI.Button(new Rect(, , , ), "Connect to Server"))
{
//连接游戏服务器
Network.Connect(ip, port);
}
}
else
{
//初始化服务器时输出信息
if (Network.peerType == NetworkPeerType.Server)
{
GUI.Label(new Rect(, , , ), "Initialization Server...");
//查看连接网络的用户数
GUI.Label(new Rect(, , , ), "Clint Count = " + Network.connections.Length.ToString());
}
//以客户端身份连接时输出信息
if (Network.peerType == NetworkPeerType.Client)
{
GUI.Label(new Rect(, , , ), "Connect to Server");
}
}
} //游戏服务器初始化正常完成时创建player
void OnServerInitialized()
{
CreatePlayer();
}
//有玩家连接服务器时调用
void OnConnectedToServer()
{
CreatePlayer();
} void CreatePlayer()
{
//随机生成玩家的初始位置
Vector3 pos = new Vector3(Random.Range(-,),,Random.Range(-,));
//网络上动态生成玩家
Network.Instantiate(player,pos,Quaternion.identity,);
} }

2)摄像机追踪逻辑

先导入SmoothFollow脚本(Import Package→Utility),并把target变量修改为public。挂在Player预设。

using UnityEngine;
using System.Collections;
//为了使用SmoothFollow脚本,需要先声明命名空间
using UnityStandardAssets.Utility; public class NetPlayerCtrl : MonoBehaviour
{
private Transform tr;
private NetworkView _networkView; void Awake ()
{
tr = GetComponent<Transform>();
_networkView = GetComponent<NetworkView>();
//查看NetworkView组件是否为自己的Player组件
if (_networkView.isMine)
{
//设置Main.Camera要追踪的对象
Camera.main.GetComponent<SmoothFollow>().target = tr;
}
}
}

禁用服务器端的玩家移动脚本,使其不要根据键盘输入值执行移动逻辑。

//如果当前玩家是远程服务器端的玩家,则禁用此脚本
this.enabled = GetComponent<NetworkView>().isMine;

3)平滑的同步处理

在检视面板,把NetPlayerCtrl脚本拖拽到NetworkView组件的Observed属性。

修改脚本,使NetworkView组件不会在每个SendRate周期都向Transform组件传送数据,而是调用NetPlayerCtrl脚本内的OnSerializeNetworkView函数。

//声明接收传送位置信息时使用的变量并设置初始值
private Vector3 currPos = Vector3.zero;
private Quaternion currRot = Quaternion.identity; void Update()
{
if (_networkView.isMine)
{
}
else//远程玩家
{
//将远程玩家平滑地移动旋转到NetworkView传送来的目标位置
tr.position = Vector3.Lerp(tr.position,currPos,Time.deltaTime * );
tr.rotation = Quaternion.Slerp(tr.rotation,currRot,Time.deltaTime * );
}
} //NetworkView组件调用的回调函数
   //BitStream stream:序列化数据并保存到数据流
    //NetworkMessageInfo info:保存从网络接收的信息
void OnSerializeNetworkView(BitStream stream,NetworkMessageInfo info)
{
if (stream.isWriting)
{
Vector3 pos = tr.position;
Quaternion rot = tr.rotation;
//数据传送
stream.Serialize(ref pos);
stream.Serialize(ref rot);
}
else
{
Vector3 revpos = Vector3.zero;
Quaternion revrot = Quaternion.identity;
//数据接收
stream.Serialize(ref revpos);
stream.Serialize(ref revrot); currPos = revpos;
currRot = revrot;
}
}

基于Unity5的TPS整理的更多相关文章

  1. 基于jQuery选择器的整理集合

    jquery对象访问1.each(callback):以每个匹配的元素作为上下文来执行一个函数,return false;停止循环;return true;跳至下一个循环. 来个实例 : 代码如下: ...

  2. SVG素材整理(原)

      why SVG? 1.矢量的国际图形标准,以后随着浏览器的发展,相信矢量会更多的出现 2.illustartor等多数矢量绘图软件可以导出为这种格式 so SVG现状   基本介绍:http:// ...

  3. 基于APM实现RPC服务和消息队列的指定消费

    本文内容是基于公司现有框架整理的一篇专利文章.该框架包含完整的一套DevOps流程,包括工单系统(容器申请.服务部署等)\配置中心\路由配置中心\服务治理平台\消息治理平台\葛朗台(基于Docker+ ...

  4. 【数据库测试工具】认识Sysbench

    本文基于课堂PPT笔记整理,主要介绍一下Sysbench及其简单使用,实验代码部分在代码中有重点注释,不另作说明. 一,基准测试 什么是数据库的基准测试? 数据库的基准测试是对数据库的性能指标进行定量 ...

  5. AppStore SDK

    ios审核被拒4.3 http://www.cocoachina.com/bbs/read.php?tid-1731757.html ios审核4.3被拒? 别担心 这几步让你的 App 顺利过审 h ...

  6. NGUI裁剪模型和粒子

    效果预览 注:Cube上附着的绿色是我添加的粒子效果. 软件环境 NGUI 3.9.x Unity 5.1 x64 相关知识 RenderTexture RenderTexture是一种特殊的纹理,它 ...

  7. Android APK如何签名

    Android项目以它的包名作为唯一标识,如果在同一设备上安装两个相同的应用,后面安装的应用就会覆盖前面安装的应用.为了避免这种情况的发生,我们需要对作为产品发布的应用进行签名. 签名其实有两个作用: ...

  8. 关于 SSV-ID: 4474 POC的分析和思考

    SSV-ID: 4474 SSV-AppDir: Discuz!漏洞 发布时间: 2008-11-21 (GMT+0800) URL:http://sebug.net/vuldb/ssvid-4474 ...

  9. 2014年3月份第3周51Aspx源码发布详情

    WPF翻书效果源码  2014-3-17 [VS2010]源码描述:WPF翻书效果源码:多点触控的一个Demo,利用鼠标可以实现图书翻页效果:适合新手学习研究. TL简单家具网新手源码  2014-3 ...

随机推荐

  1. 【转】非教育网中IPv4网络访问IPv6资源

    1. 背景知识 随着个人电脑.移动终端.乃至物联网的不断发展,有很大的IP地址需求.由于IPv4协议设计时没有料到日后网络会如此发达,IPv4网络中的IP数量相对今天的需求来说,显得捉襟见肘.加上IP ...

  2. Python set特点

    set的特点 set的内部结构和dict很像,唯一区别是不存储value,因此,判断一个元素是否在set中速度很快. set存储的元素和dict的key类似,必须是不变对象,因此,任何可变对象是不能放 ...

  3. MySQL中的锁、隔离等级和读场景

    一.导言 关于MySQL中的锁还有隔离等级这类话题,其概念性的解释早已泛滥.记住其概念,算不上什么.更重要的是思考:他们的区别和联系在哪儿,为什么会有这样的概念. 1)MySQL的锁(Lock)分为行 ...

  4. Linux-软件包管理-yum在线管理-网络yum源

    cd /etc/yum.repos.d/  切换到etc目录下面的yum.repos.d这个目录中ls   查看当前linux系统的yum源文件信息,其中CentOS-Base.repo文件为默认的y ...

  5. IE提示console未定义问题解决

    在页面加入如下代码: if (!window.console || !console.firebug){    var names = ["log", "debug&qu ...

  6. VB命令行参数分隔, 类似C语言中的main(int argc, char* argv[])

    VB6.0为了提供命令行参数的支持,提供了Command()接口,于是通过 Command() 函数可以得到所有传入的参数,但是很不友好的是,VB的所有参数都被合在了一起,成为了一个字符串,当有多个参 ...

  7. Lintcode---二叉树的最大节点

    在二叉树中寻找值最大的节点并返回. 您在真实的面试中是否遇到过这个题? Yes 样例 给出如下一棵二叉树: 1 / \ -5 2 / \ / \ 0 3 -4 -5 返回值为 3 的节点. 思路: 题 ...

  8. js 获取中文的拼音首字母

    es6 + 模块化封装 "use strict"; module.exports = { //参数,中文字符串 //返回值:拼音首字母串数组 makePy (str) { if ( ...

  9. node.js 操作excel 表格与XML文件常用的npm

    在日常工作中会经常用到把一些excel表格文件转化为json,xml,js等格式的文件,下面就是我在日常中用到的这些npm. 1.node-xlsx: node-xlsx可以把excel文件转化为上面 ...

  10. ubuntu内窗口最大最小化

    我用的dell笔记本,开始的时候,需要修改dell笔记本的BIOS,里面有一个 Funtion Key 一开始定义的行为是多媒体的,需要改成默认行为,具体记不清了, 反正知道这个,肯定知道哪里找. 然 ...