这几天跟着视频学做了一个切水果的游戏(用UGUI做的),有点粗糙记录一下。~在导入一些素材包的时候发现一些prefab丢失了,变成了白纸 ( 可能是我的unity版本有点低了╮(╯▽╰)╭)
没办法只能自己动手做几个预设了(apple lemon watermelon以及一些特效)ps:特效没接触过看了下网上的介绍自己尝试着做几个出来 - -
OK,Let's do it!
一、开始页面搭建
1、调整画布以及基本的界面布置
这里我将SoundButton和SoundSlider都放在一个空物体里面,方便管理以及锚点设置。
2、添加一个脚本用来注册点击事件、音量控制、声音播放。
ps:值得一提的是,平时我们注册点击事件都是写一个方法,然后将其拖拽到Button组件的OnClick里实现,这里可以通过监听的方式简化我们的操作。
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class UIStart : MonoBehaviour {
/// <summary>
/// 开始按钮
/// </summary>
private Button PlayButton;
/// <summary>
/// 声音按钮
/// </summary>
public Button SoundButton;
/// <summary>
/// 添加背景音乐
/// </summary>
public AudioSource Audio_Bg;
/// <summary>
///声音的图片
/// </summary>
public Sprite[] soundSprites;
private Image soundImg;
/// <summary>
/// 控制音量滑块
/// </summary>
public Slider soundSlider;
void Start () {
getComponents();
PlayButton.onClick.AddListener(PlayClick);//注册点击事件的第三种方法。监听
SoundButton.onClick.AddListener(SoundClick);
}
void Destory()
{
PlayButton.onClick.RemoveListener(PlayClick);
SoundButton.onClick.RemoveListener(SoundClick);
}
/// <summary>
/// 寻找组件
/// </summary>
private void getComponents()
{
PlayButton = transform.Find("StartButton").GetComponent<Button>();
}
/// <summary>
/// 开始按钮点击调用
/// </summary>
void PlayClick()
{
SceneManager.LoadScene("play");
}
/// <summary>
/// 声音按钮点击调用
/// </summary>
void SoundClick()
{
if (Audio_Bg.isPlaying)
{
Audio_Bg.Pause();
soundImg.sprite = soundSprites[1];
}
else
{
Audio_Bg.Play();
soundImg.sprite = soundSprites[0];
}
}
/// <summary>
/// 滑动条控制音量大小
/// </summary>
void SoundController()
{
if (soundSlider.value == 0)
{
Audio_Bg.volume = 0;
}
else if (soundSlider.value != 0)
{
Audio_Bg.volume = soundSlider.value;
}
}
void Update () {
SoundController();
}
}
public Sprite[] soundSprites;
private Image soundImg;
这两个字段用于展现静音和开启音乐的效果(2张图片)。
二、游戏界面内容
1、设置背景图片--Background
2、实现发射水果
创建一个空物体Spawner,将其摆放在Bg的中下位置也就是水果发射的初始地点,为其添加一个Box Collider组件用于销毁游戏物体节省内存。
创建一个脚本实现水果、炸弹发射。
using UnityEngine;
using System.Collections;
using UnityEngine;
//本脚本功能为产生水果、炸弹以及销毁超出屏幕的预制体
//产生水果时间、水果发射(速度、方向、反重力)、产生多个水果、
/// <summary>
/// 产生水果、炸弹
/// </summary>
public class Spawn : MonoBehaviour {
[Header("水果的预设")]
public GameObject[] Fruits;
[Header("炸弹的预设")]
public GameObject Bomb;
public AudioSource audioSource;
//产生水果的时间
float spawnTime = 3f;
//计时
float currentTime = 0f;
bool isPlaying = true;
void Update () {
if (!isPlaying)
{
return;
}
currentTime += Time.deltaTime;
if (currentTime >= spawnTime)
{
//产生多个水果
int fruitCount = Random.Range(1, 5);
for (int i = 0; i < fruitCount; i++)
{
//到时间产生水果
onSpawn(true);
}
//生成炸弹
int bombNum = Random.Range(0, 100);
if (bombNum > 70)
{
onSpawn(false);
}
currentTime = 0f;
}
}
private int tmpZ = 0; //临时存储生成体的Z坐标,前提要保证生成体的Z坐标不会超出摄像机。
/// <summary>
/// 产生水果
/// </summary>
private void onSpawn(bool isFruit)
{
//播放音乐
this.audioSource.Play();
//x范围在(-8.4,8.4)之间[生成物UI可显示的范围]
//y : transform.pos.y
float x = Random.Range(-8.4f, 8.4f);
float y = transform.position.y;
float z = tmpZ;
//保证生成体的Z坐标不会超出摄像机。(摄像机的Z轴坐标为-12.)
tmpZ -= 2;
if (tmpZ <= -10)
{
tmpZ = 0;
}
//定义一个随机数(水果号码)
int fruitIndex = Random.Range(0, Fruits.Length);
GameObject go;
if (isFruit)
{
go = Instantiate(Fruits[fruitIndex], new Vector3(x, y, z), Random.rotation) as GameObject;
}
else
{
go = Instantiate(Bomb, new Vector3(x, y, z), Random.rotation) as GameObject;
}
//给与一个反重力初始速度,飞行方向相反的
Vector3 velocity = new Vector3(-x * Random.Range(0.2f, 0.8f), -Physics.gravity.y * Random.Range(1.2f, 1.5f));
Rigidbody rigidbody = go.GetComponent<Rigidbody>();
rigidbody.velocity = velocity;
}
/// <summary>
/// 销毁产生的物体
/// </summary>
/// <param name="other"></param>
private void OnCollisionEnter(Collision other)
{
Destroy(other.gameObject);
}
}
3、生成刀痕
刀痕的生成用Line Renderer
LineRenderer里的Positions 是记录鼠标在滑动的时候记录下来的实时坐标。通过坐标来连成线。
代码中的head和last,分别是每一帧鼠标所在的位置,和上一帧鼠标所在的位置,这个的确有点绕需要自己多理解一下。(鼠标移动的时候每一帧都有变化。)
using UnityEngine;
using System.Collections;
//产生刀痕
//产生射线
public class MouseControl : MonoBehaviour {
/// <summary>
/// 直线渲染器
/// </summary>
[SerializeField]
private LineRenderer lineRenderer;
/// <summary>
/// 播放声音
/// </summary>
[SerializeField]
private AudioSource audioSource;
/// <summary>
/// 首次按下鼠标
/// </summary>
private bool firstMouseDown = false;
/// <summary>
/// 一直按住鼠标
/// </summary>
private bool onMouseDown = false;
/// <summary>
/// 用于存放渲染器位置坐标
/// </summary>
private Vector3[] positions = new Vector3[10];
/// <summary>
/// 对渲染直线坐标个数的统计
/// </summary>
private int posCount = 0;
/// <summary>
/// 存储鼠标按下的第一帧位置
/// </summary>
private Vector3 head;
/// <summary>
/// 存储上一帧鼠标所在的位置
/// </summary>
private Vector3 last;
void Update () {
if(Input.GetMouseButtonDown(0))
{
firstMouseDown = true;
onMouseDown = true;
audioSource.Play();
}
if(Input.GetMouseButtonUp(0))
{
onMouseDown = false;
}
OnDrawLine();
firstMouseDown = false;
}
/// <summary>
/// 画线
/// </summary>
private void OnDrawLine()
{
if (firstMouseDown)
{
posCount = 0;
head = Camera.main.ScreenToWorldPoint(Input.mousePosition);
last = head;
}
if (onMouseDown)
{
head = Camera.main.ScreenToWorldPoint(Input.mousePosition);
//根据首次按下时与上一次鼠标的位置的距离来判断是否画线。
if (Vector3.Distance(head, last) > 0.01f)
{
setPositions(head);
posCount++;
//发射一条射线
OnDrawRay(head);
}
last = head;
}
else
{
//放鼠标松开时,清空坐标
positions = new Vector3[10];
}
changePosition(positions);
}
/// <summary>
/// 用来存储坐标
/// </summary>
private void setPositions(Vector3 pos)
{
pos.z = 0;
if (posCount <= 9)
{
for (int i = posCount; i < 10; i++)
{
positions[i] = pos;
}
}
else
{
//坐标个数大于10,将坐标往上挤,将最新的赋值给第十个坐标[下标为9]
for (int i = 0; i < 9; i++)
{
positions[i] = positions[i + 1];
positions[9] = pos;
}
}
}
/// <summary>
/// 发射射线
/// </summary>
private void OnDrawRay(Vector3 worldPos)
{
Vector3 screenPos = Camera.main.WorldToScreenPoint(worldPos);
Ray ray = Camera.main.ScreenPointToRay(screenPos);
////如果射线检测单一物体时用以下方法
//RaycastHit hit ;
//if(Physics.Raycast(ray,out hit))
//{
// //执行内容
//}
//由于检测到的物体不止一个所以用hits[] 来检测
RaycastHit[] hits = Physics.RaycastAll(ray);
for (int i = 0; i < hits.Length; i++)
{
//Destroy(hits[i].collider.gameObject);
hits[i].collider.gameObject.SendMessage("Oncut",SendMessageOptions.DontRequireReceiver);
}
}
/// <summary>
/// 改变坐标
/// </summary>
/// <param name="positions"></param>
private void changePosition(Vector3[] positions)
{
lineRenderer.SetPositions(positions);
}
}
效果图:
4、实现水果切半以及特效。
在每一个完成的水果上添加一个脚本叫ObjectControl
using UnityEngine;
using System.Collections;
/// <summary>
/// 物体控制脚本
/// </summary>
public class ObjectControl : MonoBehaviour {
/// <summary>
/// 生成一半水果
/// </summary>
public GameObject halfFruit;
public GameObject Splash;
public GameObject SplashFlat;
public GameObject BombSplash;
public AudioClip Clip;
private bool dead = false;
public void Oncut()
{
//保证只切割一次
if (dead)
{
return;
}
if (gameObject.name.Contains("Bomb"))
{
GameObject bSplash = Instantiate(BombSplash, transform.position, Quaternion.identity)as GameObject;
ScoreEditor.Instance_.ReduceScore(20);
Destroy(bSplash,3.0f);
}
else
{
//实例化2个切半的水果
for (int i = 0; i < 2; i++)
{
GameObject go = Instantiate(halfFruit, transform.position, Random.rotation) as GameObject;
go.GetComponent<Rigidbody>().AddForce(Random.onUnitSphere * 5f, ForceMode.Impulse);
Destroy(go, 2.0f);
}
//生成特效
GameObject Splash1 = Instantiate(Splash, transform.position, Quaternion.identity)as GameObject;
GameObject Splash2 = Instantiate(SplashFlat, transform.position, Quaternion.identity) as GameObject;
Destroy(Splash1, 3.0f);
Destroy(Splash2, 3.0f);
ScoreEditor.Instance_.AddScore(10);
}
AudioSource.PlayClipAtPoint(Clip, transform.position);
Destroy(gameObject);
dead = true;
}
}
5、得分UI以及倒计时
加分与减分:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class ScoreEditor : MonoBehaviour {
public static ScoreEditor Instance_ = null;
void Awake()
{
Instance_ = this;
}
[SerializeField]
public Text txtScore;
private int score = 0;
// Update is called once per frame
void Update () {
}
public void AddScore(int Score)
{
score += Score;
txtScore.text = score.ToString();
}
public void ReduceScore(int Score)
{
score -= Score;
if (score <= 0)
{
SceneManager.LoadScene("Gameover");
}
txtScore.text = score.ToString();
}
}
倒计时:
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
/// <summary>
/// 倒计时脚本
/// </summary>
public class showTime : MonoBehaviour
{
public float time_All = 300;//计时的总时间(单位秒)
public float time_Left;//剩余时间
public bool isPauseTime = false;
public Text time;
// Use this for initialization
void Start()
{
time_Left = time_All;
}
// Update is called once per frame
void Update()
{
if (!isPauseTime)
{
if (time_Left > 0)
StartTimer();
else
{
SceneManager.LoadScene("Gameover");
}
}
}
/// <summary>
/// 获取总的时间字符串
/// </summary>
string GetTime(float time)
{
return GetMinute(time) + GetSecond(time);
}
/// <summary>
/// 获取小时
/// </summary>
string GetHour(float time)
{
int timer = (int)(time / 3600);
string timerStr;
if (timer < 10)
timerStr = "0" + timer.ToString() + ":";
else
timerStr = timer.ToString() + ":";
return timerStr;
}
/// <summary>
///获取分钟
/// </summary>
string GetMinute(float time)
{
int timer = (int)((time % 3600) / 60);
string timerStr;
if (timer < 10)
timerStr = "0" + timer.ToString() + ":";
else
timerStr = timer.ToString() + ":";
return timerStr;
}
/// <summary>
/// 获取秒
/// </summary>
string GetSecond(float time)
{
int timer = (int)((time % 3600) % 60);
string timerStr;
if (timer < 10)
timerStr = "0" + timer.ToString();
else
timerStr = timer.ToString();
return timerStr;
}
}
三、结束界面与数据保存
代码:
using UnityEngine;
using System.Collections;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class Gameover : MonoBehaviour {
public Text text;
public Text nowScore;
public Text bestScore;
void Start()
{
getScore();
}
void Update()
{
}
private void getScore()
{
text.text = ScoreEditor.Instance_.txtScore.text;
bestScore.text = text.text;
bstScore(int.Parse(text.text));
}
private void bstScore(int NowScore)
{
int bstScore = PlayerPrefs.GetInt("sr", 0);
Debug.Log(bstScore);
if (NowScore > bstScore)
{
bstScore = NowScore;
}
PlayerPrefs.SetInt("sr", bstScore);
bestScore.text = bstScore.ToString();
}
public void OnClick()
{
SceneManager.LoadScene("play");
}
}
NowScore对应上图中“你的分数”里面的text
BestScore对应上图中“你的最高分数”里面的text
结束,写的很乱,只是以后自己方便看-。- 哈哈哈。