您的位置:首页 > 移动开发 > IOS开发

u3d5第一人称射击游戏(C#脚本)完整版并在iOS9.3系统上真机运行

2016-04-27 16:45 615 查看
参考资料:《Unity3D\2D手机游戏开发》(第二版) +   百度

涉及U3D的功能有:摄像机控制、物理、动画、智能寻路等。

开发工具:Unity3D5.3.4,VS2015,VMplayer12+MacOSX10.11+XCode7.3(我是Windos笔记本用的是苹果虚拟机,MAC真机不需要虚拟机,直接在appstore下载XCode最新版本就可以了,MAC最新版+Xcode最新版本可以直接真机调试运行)+OS9.3系统iphone

说一下U3D,想要在ios上调试的话就要在下载的时候勾选ios的插件,如果你之前没勾选的话就需要重新下载,可以直接勾选ios其他的都不勾选,我就是重新下载的unity。

制作的是完整版的游戏,包括开始游戏界面,排行榜界面,游戏介绍界面。。。

游戏制作中遇到的问题(下文都将一一解答):

1.场景切换问题:因为是自学,参考书上面只说了游戏的制作,并没有场景的切换,也就是开始游戏,游戏介绍的场景界面这些

2.怎么编译到iOS

3.VS2015(C#脚本编辑器) 使用30天后无法正常使用

4.Win7系统里面调好的尺寸在iphone上并不可用,后来我根据iphone改变的尺寸,下面我的截图是win7上unity的截图,所以尺寸大家不要介意

游戏中遇到的未解决问题

.我游戏里面用到的摇杆是EasyTouch3插件,然后添加摇杆后,start游戏可以正常使用摇杆,但是一旦我选择重新加载游戏或者退出游戏再开始游戏时,就会报错MissingReferenceException: The object of type 'Transform' has been destroyed but you are still trying to access it.Your script should either check
if it is null or you should not destroy the object.

这一问题困扰了我很多天,我也在网上找了好多资料,但是最终没有找到解决的办法,我试过的解决办法如下:

1.将摇杆放到Player游戏体下层不可行。

2.删除控制摇杆脚本里面的OnEnable方法不可行。

3.添加OnDestory方法不可行

    void OnDestory() {

        EasyJoystick.On_JoystickMove -= OnJoystickMove;

        EasyJoystick.On_JoystickMoveEnd -= OnJoystickMoveEnd;

    }

4.将两个摇杆的脚本都添加到Player游戏体上不可行

5.在GameManager中,点击重新开始或退出游戏时destory摇杆游戏体

Can't destroy Transform component of 'Joystick'. If you want to destroy the game object, please call 'Destroy' on the game object instead. Destroying the transform component is not allowed.

不可行!!

6.添加脚本为不可删除的cantdestory(this);不可行!

所以我放弃了解决这个bug。

完整游戏工程http://download.csdn.net/detail/v7595v/9493631   需要下载积分5分解压后直接用unity打开

游戏资源http://download.csdn.net/detail/v7595v/9493622  需要下载积分1分解压后导入工程 Assets->Import New Assets

U3D摇杆插件 EasyTouch下载地址 http://download.csdn.net/detail/v7595v/9504461
下面开始教程

首先在win7系统使用U3D制作游戏,之后再编译成iOS,导入虚拟机中iPhone真机调试

1开始游戏的场景:

打开Unity3D新建一个工程,包括起名字和存储地址等等,不多说

我的开始游戏的场景是这样的


背景图案+3个button按钮

背景图案:1在场景里面新建一个Plane      GameObject->3DObject->Plane    选中Plane,在Inspector下的Transform设置Reset(养成良好的习惯,新建一个GameObject就将其Transform,Reset一下,然后在调整你想要的位置)

    2 然后在project新建一个材质球(project里面的文件最好统一分类,脚本文件夹,材质球图片文件夹等等),将材质球的Shader选为LegacyShader->Diffuse

    3引入想要做背景的图片右键project下面保存想要保存图片的文件夹(文件夹自己创建或是默认文件夹,但是要有良好的分类习惯)

    4然后选中图片,拖入到材质球的select框里面,或者直接在材质球select想要的图片

    5选中之前创建的Plane,将材质球拖到Plane上面,这样Plane就能显示我们想要的图片了

接下来调整摄像机,让其对准Plane,如下图,将MainCamera的Projection设置成Oithograhpic(这是将摄像机垂直向下)然后调整摄像机的位置,高度,适当调整Plane的尺寸,然后在Game界面浏览一下,像我这样子就可以了。



接下来就是用脚本控制显示游戏名称,和开始游戏、游戏介绍、排行榜的三个button了

在场景里新建一个空游戏体GameObject->CreateEmpty然后在project新建一个C#脚本命名GameManagerStart,将脚本拖到空游戏体上(默认的名字就是GameObject,我没改名,所以上图显示的GameObject就是我创建的空游戏体),接下来就是理解脚本了。

using UnityEngine;
using System.Collections;

public class GameManagerStart : MonoBehaviour {

// Use this for initialization
void Start () {

}

// Update is called once per frame
void Update () {

}

void OnGUI() {
GUI.skin.label.fontSize = 240;									//设置label的字体大小
GUI.skin.label.alignment = TextAnchor.UpperCenter;						//设置label的对齐方式
GUI.Label(new Rect(0, 20, Screen.width, 1000), "<color=red>密  室  惊  魂</color>");		//游戏名称的label
GUI.skin.button.fontSize = 100;
if (GUI.Button(new Rect(Screen.width * 0.5f - 200, Screen.height * 0.3f, 400, 140), "开始闯关")) {
Application.LoadLevel("demo");
}
if (GUI.Button(new Rect(Screen.width * 0.5f - 200, Screen.height * 0.5f, 400, 140), "游戏介绍")) {
Application.LoadLevel("Instruction");
}
if (GUI.Button(new Rect(Screen.width * 0.5f - 200, Screen.height * 0.7f, 400, 140), "最高得分")) {
Application.LoadLevel("paihangbang");
}
}
}



脚本解析:
using UnityEngine;
using System.Collections;
这两行代码引入两个类 Start()Updata()函数都是C#脚本开始便自带的函数

Start()是程序开始的时候执行一次,Updata()是每一帧都执行,在此脚本中我都没有用到这两个函数

OnGUI()函数是显示UI的函数,也是每一帧都执行的函数

if (GUI.Button(new Rect(Screen.width * 0.5f - 200, Screen.height * 0.3f, 400, 140), "开始闯关")) {
Application.LoadLevel("demo");
}
这段代码是直接创建一个button,并设置button的rect和名称,如果点击button后执行加载场景demo的操作,3个button对应三个场景,现在还没有其它的场景,可以先将加载场景的代码注释掉,等创建了其它场景再解开注释。

运行一下就可以看到和我上面图片一样的开始游戏界面效果了,保存场景,起名为start。File->SaveScene,保存到project里面,就是刚开始创建游戏的位置,找到project里面你想保存的文件夹,或新建一个文件夹专门保存游戏场景。下图是我保存场景的地方



2游戏介绍场景,最高得分场景

跟开始场景差不多

就是新建场景File->new Scene

然后按照上面start场景的操作做就可以了

游戏介绍场景加代码:



using UnityEngine;
using System.Collections;

public class GameManagerInstrection : MonoBehaviour {

// Use this for initialization
void Start () {

}

// Update is called once per frame
void Update () {

}

void OnGUI() {
GUI.skin.label.alignment = TextAnchor.UpperCenter;
GUI.skin.label.fontSize = 100;
GUI.Label(new Rect(0, 200, Screen.width, Screen.height), "<color=white>一款模拟CS</color>");
GUI.Label(new Rect(0, 340, Screen.width, Screen.height), "<color=white>3D射击游戏</color>");
GUI.Label(new Rect(0, 490, Screen.width, Screen.height), "<color=white>Here we go?</color>");
GUI.skin.button.fontSize = 100;
if (GUI.Button(new Rect(Screen.width * 0.5f-140, Screen.height * 0.7f, 280, 100), "GO")) {
Application.LoadLevel("start");
}
}
}


最高得分的场景就不截图了,下面是最高得分的GameManager的代码

最高分的场景我原来是想做分数排行的,固定显示一个最高分数,然后每次有新的记录的时候,再添加到那几个分数当中,但是后来没有做到,我就只显示了一个最高分

这里说一下怎么保存游戏最高得分,利用PlayerPrefs的set和get方法,在后面的demo场景中会set游戏得分,在本场景中get得分,然后显示出来,也只是临时存储,再次启动游戏记录也就没有了,我查过最好是用XML存储游戏分数,但是就算XML存储游戏分数,我显示并替换分数的逻辑没有想明白,因为我是用OnGUI来显示分数的,但是OnGUI是每一帧都执行的,所以,应该不用OnGUI来显示分数。如果谁有好的想法的话可以加我微信,谢谢了。微信:lidi55

using UnityEngine;
using System.Collections;

public class GameManagerPHBang : MonoBehaviour {
Transform m_transform;
int m_score = 0;

System.DateTime now;

void Start () {
m_transform = this.transform;
m_score = PlayerPrefs.GetInt("Score",0);
}

void Update () {

}

void OnGUI() {
GUI.skin.button.fontSize = 100;
if (GUI.Button(new Rect(Screen.width * 0.8f, Screen.height * 0.8f, 280, 100), "GO"))
{
Application.LoadLevel("start");
}
GUI.skin.label.alignment = TextAnchor.UpperCenter;
GUI.skin.label.fontSize = 30;
now = System.DateTime.Now;

string date = now.Year.ToString() + "/" + now.Month.ToString() + "/" + now.Day.ToString() +" "+m_score;
GUI.Label(new Rect(0, Screen.height*0.1f, 400, 80), date);
}
}


接下来是重头戏:demo场景
demo场景里面会用到很多资源,我把需要用到的游戏资源上传一份,上面有链接。

新建demo场景,并且导入游戏资源后,选择level下的3个模型,然后在菜单栏选择Component->Physics->MeshCollider为其添加多边形碰撞体组建,现在场景模型既可以用于显示,也可以用于碰撞。然后把3个模型的MeshCollider组件的IsTrigger取消勾选,is Trigger:如果勾选了该属性,那么该物体就是一个虚体,有形而无实,不受力的作用,其它对象可以穿过它。


下面我们制作主角

因为是第一人称射击,所以主角不可见,弄个枪就行了,但是还是要为主角创建碰撞体并控制其移动。

创建一个空游戏体,起名为Player,将它的Tag也设为Player(点击右上角的Tag,AddTag就可以添加Tag了),这就是主角了。

在菜单栏选择Component->Physics->CharacterController为主角添加游戏控制器组件,该组件可以让我们控制主角的移动,也可以产生碰撞,比如行走时不会穿到墙里。

为主角添加Rigidbody组件,取消UseGravity去掉重力模拟,并选中IsKinematic使其不受物理演算影响,这样就可以用脚本控制其移动。CharacterController组件中可以调整碰撞体的位置和大小,如下图。



是第一人称射击游戏,所以摄像机就是主角的视角,所以在后面的脚本中会控制摄像机跟随主角的视角,摄像机还要显示一把枪出来,这把枪在视觉上代替主角。

将摄像机的Camera组件下的ClippingPlane/near设置为0.1,使其可以看到更近的物体,然后找到引入的文件M16.Prefab,这是预设好的武器Prefab,是一个简单的枪支模型。将其拖入到Hierarchy窗口的摄像机层级下,使其成为摄像机的子物体。然后选择Prefab下一级的枪模型weapon,调整他的位置和角度,在CameraPreview中预览效果,直到满意为止。



直接上Player的脚本,脚本要拖到Player游戏体上

using UnityEngine;
using System.Collections;

public class Player : MonoBehaviour {
public Transform m_transform; //定义自己的Transform
Transform m_muzzlepoint;<span style="white-space:pre"> </span>//这个是加在摄像机下面枪模型的枪口位置,主角射击的时候用到的Transform
public LayerMask m_layer;<span style="white-space:pre"> </span>//定义射击时,可以击中的碰撞层
public Transform m_fx;<span style="white-space:pre"> </span>//射击特效
public AudioClip m_audio;<span style="white-space:pre"> </span>//射击声音
public CharacterController m_ch;<span style="white-space:pre"> </span>//主角的CharacterController组件

float m_movSpeed = 8.0f;<span style="white-space:pre"> </span>//主角的移动速度
float m_gravity = 0.1f;<span style="white-space:pre"> </span>//主角受到的重力
public int m_life = 5;<span style="white-space:pre"> </span>//主角的起始生命

Transform m_camTransform;<span style="white-space:pre"> </span>//摄像机Transform
float m_camHeight = 0.5f;<span style="white-space:pre"> </span>//定义的摄像机的高度,即主角的视角高度<span style="white-space:pre"> </span>

// Use this for initialization
void Start () {
m_transform = this.transform;<span style="white-space:pre"> </span>//将Player的Transform给m_transform
m_ch = GetComponent<CharacterController>();
<span style="white-space:pre"> </span>//获取CharacterController组件
m_camTransform = Camera.main.transform;<span style="white-space:pre"> </span>//获取摄像机,将摄像机的Transform给m_camTransform
Vector3 pos = m_transform.position;<span style="white-space:pre"> </span>//获取主角面向的方向
pos.y += m_camHeight;<span style="white-space:pre"> </span>//主角面向的方向加上摄像机的高度
m_camTransform.position = pos;<span style="white-space:pre"> </span>//然后赋值给摄像机面向的方向

m_camTransform.rotation = m_transform.rotation; <span style="white-space:pre"> </span>//主角的旋转角度给摄像机的旋转角度

m_muzzlepoint = m_camTransform.FindChild("M16/weapon/muzzlepoint").transform;//获取枪<span style="font-family: Arial, Helvetica, sans-serif;">头位置</span>
<span style="font-family: Arial, Helvetica, sans-serif;">}</span>
// Update is called once per frame
void Update () {
if (m_life <= 0||Time.timeScale == 0)
{
return;
}
Control();
}

public void Shoot() {
if (Time.timeScale == 1) {
this.GetComponent<AudioSource>().PlayOneShot(m_audio);
GameManager.Instance.SetAmmo(1);
RaycastHit info;
bool hit = Physics.Raycast(m_muzzlepoint.position, m_camTransform.TransformDirection(Vector3.forward),
out info, 100, m_layer);
if (hit) {
if (info.transform.tag.CompareTo("enemy") == 0)
{
Enemy enemy = info.transform.GetComponent<Enemy>();
enemy.OnDamage(1);
}
Instantiate(m_fx, info.point, info.transform.rotation);
}
}
}

void Control ()<span style="white-space:pre">		</span>//在Updata()里面调用,所以以后开始游戏的每一帧都都让摄像机的position和主角的一样
{<span style="white-space:pre">			</span>//<span style="font-family: Arial, Helvetica, sans-serif;">WASD控制方向,但是移植到iPhone上面以后就需要摇杆控制了</span>
float xm = 0;
float ym = 0;
float zm = 0;
//重力
ym -= m_gravity * Time.deltaTime;

if (Input.GetKey(KeyCode.W))
{
zm += m_movSpeed * Time.deltaTime;
}
else if (Input.GetKey(KeyCode.S))
{
zm -= m_movSpeed * Time.deltaTime;
}

if (Input.GetKey(KeyCode.A))
{
xm -= m_movSpeed * Time.deltaTime;
}
else if (Input.GetKey(KeyCode.D))
{
xm += m_movSpeed * Time.deltaTime;
}

m_ch.Move(m_transform.TransformDirection(new Vector3(xm, ym, zm)));
//Control()方法到这为止,前面是WASD控制方向,但是移植到iPhone上面以后就需要摇杆控制了,所以下面的代码才是主要的
//是控制摄像机跟随主角的视角
Vector3 pos = m_transform.position;
pos.y = m_camHeight;
m_camTransform.position = pos;
}

void OnGizmos() {
//在场景中显示一个图标来标识一下这个是主角,运行游戏后没有实际作用
Gizmos.DrawIcon(transform.position, "Spawn.tif");
}
<span style="white-space:pre">	</span>//下面是主角和敌人交互时的函数,如果敌人攻击了主角,主角生命值减少
public void OnDamage (int damage) {
m_life -= damage;
GameManager.Instance.SetLife(m_life);//GameManager控制游戏屏幕显示的一些东西,后面会说到它
}
}

脚本解析:

开始的时候要设置摄像机的角度、位置(还有高度)与player相同

void Update () {
if (m_life <= 0||Time.timeScale == 0)
{
return;
}
Control();
}
如果生命值小于0,让timeScale为0,在后面的GameManager里面设置了,如果timeScale为0时,就弹出Game Over等,如下就是GameManager代码

if (m_player.m_life <= 0)
{
Time.timeScale = 0;
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUI.skin.label.fontSize = 120;
GUI.Label (new Rect(0, -160, Screen.width, Screen.height), "Game Over");

GUI.skin.button.fontSize = 50;
if (GUI.Button(new Rect(Screen.width*0.5f-150, Screen.height*0.4f, 300, 60), "Try again"))
{
Application.LoadLevel("demo");
}
if (GUI.Button(new Rect(Screen.width * 0.5f-150, Screen.height * 0.6f, 300, 60), "退出游戏")) {
// m_gameManagerPHB.SetPlayerScore();
PlayerPrefs.SetInt("Score", m_score);
Application.LoadLevel("start");
}
}
Control();
函数是控制摄像机旋转角度与位置和主角相同,还有就是控制主角的移动和视角调制,前面的代码里面是包含WASD控制方向的,还有鼠标控制的代码如下,但是最后要移植到手机上这俩个方法是不行的,需要添加摇杆,添加摇杆的方法在后面说。

/*
float rh = Input.GetAxis("Mouse X");
float rv = Input.GetAxis("Mouse Y");
m_camRot.x -= rv;
m_camRot.y += rh;
m_transform.eulerAngles = m_camRot;
*/
这是鼠标控制旋转,但是要在start()函数里面锁定鼠标,Screen.lockCursor = true;

shoot函数是射击函数,在GameManager脚本里面进行调用,因为在GameManager脚本里面我放了射击按钮

如果timeScale为1的时候,就可以进行射击

获取AudioSourse组件调用playOneShoot函数用来发出声音,audio参数是在游戏体上添加的,想要在游戏体上添加或调整参数就赢该将属性设置成public

GameManager.Instance.SetAmmo(1);就是让子弹的数量减一,射击当然要减了。

bool hit = Physics.Raycast(m_muzzlepoint.position, m_camTransform.TransformDirection(Vector3.forward),
out info, 100, m_layer);
if (hit) {
if (info.transform.tag.CompareTo("enemy") == 0)
{
Enemy enemy = info.transform.GetComponent<Enemy>();
enemy.OnDamage(1);
}


射线射击,如果射到敌人,敌人生命减一

Instantiate(m_fx, info.point, info.transform.rotation);
射击特效

敌人脚本:

using UnityEngine;
using System.Collections;

public class Enemy : MonoBehaviour {
Transform m_transform;
Player m_player;
protected EnemySpawn m_spawn;
protected EnemySpawn1 m_spawn2;

Animator m_ani;
NavMeshAgent m_agent;

public float m_movSpeed = 0.5f;
float m_rotSpeed = 120;
float m_timer = 2;
int m_life = 15;

// Use this for initialization
void Start () {
m_transform = this.transform;
m_ani = this.GetComponent<Animator>();//动画组件
m_player = GameObject.FindGameObjectWithTag("Player").GetComponent<Player>();
m_agent = GetComponent<NavMeshAgent>();//寻路组件

// m_agent.SetDestination(m_player.m_transform.position);//开始就向player进行寻路
}

// Update is called once per frame
void Update () {
if (m_player.m_life <= 0)
return;
if (GameManager.Instance.level == 3)
m_movSpeed = 100.0f;//这是第三个难度,提高敌人移动速度
AnimatorStateInfo stateInfo = m_ani.GetCurrentAnimatorStateInfo(0);//当前的动画状态
//下面是敌人的三个状态时的处理代码
if (stateInfo.nameHash == Animator.StringToHash("Base Layer.idle") &&
! m_ani.IsInTransition(0))//处于待机状态且不处于孤独状态
{
m_ani.SetBool("idle", false);
m_timer -= Time.deltaTime;
if (m_timer > 0)
return;

if (Vector3.Distance(m_transform.position, m_player.m_transform.position) < 5f)
{
m_ani.SetBool("attack", true);
}
else
{
m_timer = 1;

m_agent.SetDestination(m_player.m_transform.position);
m_ani.SetBool("run", true);
}
}

if (stateInfo.nameHash == Animator.StringToHash("Base Layer.run") &&
! m_ani.IsInTransition(0))
{
m_ani.SetBool("run", false);

m_timer -= Time.deltaTime;
if (m_timer < 0)
{
m_agent.SetDestination(m_player.m_transform.position);
m_timer = 1;

if (Vector3.Distance(m_transform.position, m_player.m_transform.position) <= 5f)
{
m_agent.ResetPath();
m_ani.SetBool("attack", true);
}
}
}

if (stateInfo.nameHash == Animator.StringToHash("Base Layer.attack") &&
! m_ani.IsInTransition(0))
{
RotateTo();
m_ani.SetBool("attack", false);

if (stateInfo.normalizedTime >= 1.0f)
{
m_ani.SetBool("idle", true);
m_timer = 2;
m_player.OnDamage(1);
}
}

if (stateInfo.nameHash == Animator.StringToHash("Base Layer.death")
&& ! m_ani.IsInTransition(0))
{
if (stateInfo.normalizedTime >= 1.0f)
{
OnDeath();
//GameManager.Instance.SetScore(100);
//Destroy(gameObject);
}
}
}

void RotateTo () {    //转向Player
Vector3 oldangle = m_transform.eulerAngles;

m_transform.LookAt(m_player.m_transform);
float target = m_transform.eulerAngles.y;

float speed = m_rotSpeed * Time.deltaTime;
float angle = Mathf.MoveTowardsAngle(oldangle.y, target, speed);
m_transform.eulerAngles = new Vector3(0, angle, 0);
}

public void OnDamage (int damage) {          //减少生命值
m_life -= damage;
if (m_life <= 0)
{
m_ani.SetBool("death", true);
}
}

public void Init(EnemySpawn spawn) {     //生成器1的计数器
m_spawn = spawn;
m_spawn.m_enemyCount++;
}

public void Init2(EnemySpawn1 spawn) {<span style="white-space:pre">	</span>//生成器2的计数器
m_spawn2 = spawn;
m_spawn2.m_enemyCount++;

}

public void OnDeath () {<span style="white-space:pre">			</span>//死亡
if (GameManager.Instance.level == 1)
m_spawn.m_enemyCount--;
else if (GameManager.Instance.level == 2||GameManager.Instance.level==3)
m_spawn2.m_enemyCount--;
GameManager.Instance.SetScore(100);
Destroy (this.gameObject);
}
}


两个生成器

等级1的时候的生成器:

using UnityEngine;
using System.Collections;

public class EnemySpawn : MonoBehaviour {
public Transform m_enemy;
public int m_enemyCount = 0;
public int m_maxEnemy =3;

protected Transform m_transform;

// Use this for initialization
void Start () {
m_transform = this.transform;
}

// Update is called once per frame
void Update () {

}

public void CreatEnemy() {
if (m_enemyCount >= m_maxEnemy)
return;
Transform obj = (Transform)Instantiate(m_enemy, m_transform.position, Quaternion.identity);
Enemy enemy = obj.GetComponent<Enemy>();
enemy.Init(this);
}

void OnDrawGizmos () {
Gizmos.DrawIcon(transform.position, "item.png", true);
}
}


等级2的时候的生成器:

using UnityEngine;
using System.Collections;

public class EnemySpawn1 : MonoBehaviour {
public Transform m_enemy;
public int m_enemyCount = 0;
public int m_maxEnemy = 8;
protected Transform m_transform;

// Use this for initialization
void Start () {
m_transform = this.transform;
}

// Update is called once per frame
void Update () {

}

public void CreatEnemy()
{
if (m_enemyCount >= m_maxEnemy)
return;
Transform obj = (Transform)Instantiate(m_enemy, m_transform.position, Quaternion.identity);
Enemy enemy = obj.GetComponent<Enemy>();
enemy.Init2(this);

}
void OnDrawGizmos()
{
Gizmos.DrawIcon(transform.position, "item.png", true);
}
}


GameManager:

using UnityEngine;
using System.Collections;

public class GameManager : MonoBehaviour {
Transform m_transform;
public static GameManager Instance = null;
public int m_score = 0;
static int m_hiscore = 0;
int m_ammo = 100;
public float m_timer_shoot = 0.1f;
public float m_timer_creatEnemy = 0;
public Texture img;
public int level = 0;

Player m_player;
Enemy m_enemy;
EnemySpawn m_enemySpawn;
EnemySpawn1 m_enemySpawn1;
//GameManagerPHBang m_gameManagerPHB;

GUIText txt_ammo;
GUIText txt_hiscore;
GUIText txt_life;
GUIText txt_score;
// Use this for initialization
void Start () {
m_transform = this.transform;
Instance = this;
m_player = GameObject.FindGameObjectWithTag("Player").GetComponent<Player>();
m_enemySpawn = GameObject.FindGameObjectWithTag("EnemySpawn").GetComponent<EnemySpawn>();
m_enemySpawn1 = GameObject.FindGameObjectWithTag("EnemySpawn1").GetComponent<EnemySpawn1>();
foreach (Transform t in this.transform.GetComponentsInChildren<Transform>()) {
if (t.name.CompareTo("txt_ammo") == 0)
{
txt_ammo = t.GetComponent<GUIText>();
}
else if (t.name.CompareTo("txt_hiscore") == 0)
{
txt_hiscore = t.GetComponent<GUIText>();
}
else if (t.name.CompareTo("txt_life") == 0)
{
txt_life = t.GetComponent<GUIText>();
}
else if (t.name.CompareTo("txt_score") == 0) {
txt_score = t.GetComponent<GUIText>();
}
}
<span style="font-family: Arial, Helvetica, sans-serif;">       <img src="https://img-blog.csdn.net/20160427163004185?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" /></span>
        Time.timeScale = 1;
}

// Update is called once per frame
void Update () {
if (Time.timeScale > 0 && Input.GetKeyDown(KeyCode.Escape))//游戏中按ESC
{
Time.timeScale = 0;
Screen.lockCursor = false;//前面要是没锁定鼠标,这里也不需要解锁鼠标
}
m_timer_creatEnemy -= Time.deltaTime;
if (m_timer_creatEnemy <= 0)
{
m_timer_creatEnemy = Random.value * 15.0f;
if (m_timer_creatEnemy < 5)
m_timer_creatEnemy = 5;
if (m_score < 1000)
{
level = 1;
m_enemySpawn.CreatEnemy();
}
else if (m_score>=1000 && m_score< 2000)
{
level = 2;
m_enemySpawn1.CreatEnemy();

}
else if (m_score >= 2000)
{
level = 3;
//Enemy.Instance.m_movSpeed = 5.0f;
m_enemySpawn1.CreatEnemy();
}
}
}

public void SetScore (int score) {
m_score += score;

if (m_score > m_hiscore)
{
m_hiscore = m_score;
}

txt_score.text = "Score "+m_score;
txt_hiscore.text = "High Score "+m_hiscore;
}

public void SetAmmo (int ammo) {
m_ammo -= ammo;

if (m_ammo <= 0)
{
m_ammo = 100 - m_ammo;
}
txt_ammo.text = m_ammo.ToString() + "/100";
}

public void SetLife (int life) {
txt_life.text = life.ToString();
}

void OnGUI () {
GUI.skin.label.fontSize = 30;
GUI.skin.label.alignment = TextAnchor.LowerCenter;
GUI.Label(new Rect(0,0,Screen.width,Screen.height), "Level" + level);

GUI.skin.label.fontSize = 30;
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
if (GUI.Button(new Rect(30, 0, 100, 30), "。。。") && Time.timeScale > 0)
{
Time.timeScale = 0;
Screen.lockCursor = false;
}

if (Time.timeScale == 0 && m_player.m_life>0) {
GUI.skin.label.alignment = TextAnchor.MiddleCenter;
GUI.skin.label.fontSize = 120;
if (GUI.Button(new Rect(Screen.width * 0.5f-150, Screen.height * 0.3f, 300, 60), "继续游戏")) {
Time.timeScale = 1;
//Screen.lockCursor = true;
}
if (GUI.Button(new Rect(Screen.width * 0.5f - 150, Screen.height * 0.5f, 300, 60), "重新开始")) {
//Destroy(joy1.transform);
Application.LoadLevel("demo");
}
if (GUI.Button(new Rect(Screen.width * 0.5f-150, Screen.height * 0.7f, 300, 60), "退出游戏")) {
//m_gameManagerPHB.SetPlayerScore();
PlayerPrefs.SetInt("Score",m_score);
Application.LoadLevel("start");
//Application.Quit();这行代码控制直接退出整个游戏了
}
}

if (m_player.m_life <= 0) { Time.timeScale = 0; GUI.skin.label.alignment = TextAnchor.MiddleCenter; GUI.skin.label.fontSize = 120; GUI.Label (new Rect(0, -160, Screen.width, Screen.height), "Game Over"); GUI.skin.button.fontSize = 50; if (GUI.Button(new Rect(Screen.width*0.5f-150, Screen.height*0.4f, 300, 60), "Try again")) { Application.LoadLevel("demo"); } if (GUI.Button(new Rect(Screen.width * 0.5f-150, Screen.height * 0.6f, 300, 60), "退出游戏")) { // m_gameManagerPHB.SetPlayerScore(); PlayerPrefs.SetInt("Score", m_score); Application.LoadLevel("start"); } }
if (GUI.RepeatButton(new Rect(Screen.width * 0.8f, Screen.height * 0.7f, 100,100), img) && Time.timeScale==1) {
m_timer_shoot -= Time.deltaTime;
if (m_timer_shoot <= 0) {
m_player.Shoot();
m_timer_shoot = 0.1f;
}
}
}
}


接下来就是两个摇杆的代码了,参考博客:

点击打开链接

using UnityEngine;
using System.Collections;

public class JoysticController : MonoBehaviour {
Transform m_transform;
Player m_player;
// float m_movSpeed_walk = 6.0f;
//float m_movSpeed_run = 10.0f;
// Use this for initialization
void Start () {
m_transform = this.transform;
m_player = GameObject.FindGameObjectWithTag("Player").GetComponent<Player>();
//m_player = this.GetComponent<Player>();

}

// Update is called once per frame
void Update () {
//DontDestroyOnLoad(this);
}
void OnEnable()
{
EasyJoystick.On_JoystickMove += OnJoystickMove;
EasyJoystick.On_JoystickMoveEnd += OnJoystickMoveEnd;
}

//移动摇杆结束
void OnJoystickMoveEnd(MovingJoystick move)
{
//停止时,角色恢复idle
if (move.joystickName == "Joystick")
{
//animation.CrossFade("idle");
}
}

//移动摇杆中
void OnJoystickMove(MovingJoystick move)
{
if (move.joystickName != "Joystick")
{
return;
}
//获取摇杆中心偏移的坐标
float joyPositionX = move.joystickAxis.x;
float joyPositionY = move.joystickAxis.y;

float mx = 0;
float my = 0;
float mz = 0;
mx += joyPositionX * Time.deltaTime*10;
my -= 0.1f * Time.deltaTime;
mz += joyPositionY * Time.deltaTime*10;
m_player.m_ch.Move(m_player.m_transform.TransformDirection(new Vector3(mx, my, mz)));
//this.GetComponent<CharacterController>().Move(m_transform.TransformDirection(new Vector3(mx, my, mz)));
//DontDestroyOnLoad(this);
}

void OnDestory() {
EasyJoystick.On_JoystickMove -= OnJoystickMove;
EasyJoystick.On_JoystickMoveEnd -= OnJoystickMoveEnd;
}

}

using UnityEngine;
using System.Collections;

public class JoysticController2 : MonoBehaviour {
Transform m_transform;
Player m_player;
Transform m_camTransform;
Vector3 m_camRot;
float m_camHeight = 0.5f;

// Use this for initialization
void Start () {
m_transform = this.transform;
m_player = GameObject.FindGameObjectWithTag("Player").GetComponent<Player>();
m_camTransform = Camera.main.transform;
m_camRot = m_camTransform.eulerAngles;
}

// Update is called once per frame
void Update () {

}
void OnEnable()
{
EasyJoystick.On_JoystickMove += OnJoystickMove;
EasyJoystick.On_JoystickMoveEnd += OnJoystickMoveEnd;
}

//移动摇杆结束
void OnJoystickMoveEnd(MovingJoystick move)
{
//停止时,角色恢复idle
if (move.joystickName == "Joystick2")
{
//animation.CrossFade("idle");
}
}

void OnJoystickMove(MovingJoystick move)
{
if (move.joystickName != "Joystick2")
{
return;
}
//获取摇杆中心偏移的坐标
float joyPositionX = move.joystickAxis.x;
float joyPositionY = move.joystickAxis.y;
m_camRot.x -= joyPositionY * 0.8f;
m_camRot.y += joyPositionX * 0.8f;
m_camTransform.eulerAngles = m_camRot;
m_player.m_transform.eulerAngles = m_camTransform.eulerAngles;
}
void OnDestory()
{
EasyJoystick.On_JoystickMove -= OnJoystickMove;
EasyJoystick.On_JoystickMoveEnd -= OnJoystickMoveEnd;
}
}
我是自己创建的脚本,然后将脚本添加到两个摇杆上面了。但是有bug,就是我前面提到的未解决bug,如果你看完了,并且有解决bug的办法,加我微信吧lidi55,请指点一下我。

解决最开始所提到的问题:

1,场景切换的话看我的GameManager代码,里面的Application.LoadLevel("场景名");前提是你有一个以上的场景可以切换对吧,如何创建场景前面也已经说了。

2.怎么编译到ios呢,需要下载带ios版本编译插件的unity,



我这个就是可以编译到ios的unity,是我重新下载的,buildseting之后我可以选择ios那个选项,不可以选择的重新下载unity,一定要勾选ios,我现在有两个unity,一个是默认勾选,一个是只勾选了ios插件



3.我的VS2015是下载第一个unity的时候自带的社区版的VS,是免费的,但是需要登录,去微软官网创建一个微软的邮箱登陆上就可以了,我第一天登录的时候网不好,没登上,我又是翻墙,又是下载win10的,还是没登录上,第二天莫名其妙就登陆上了。。。。。。。

4.尺寸大家自己掌握吧 ,我的UI基本靠蒙。。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息