是跟着Siki上的网站进行的,整理思路,原网址如下:
http://www.sikiedu.com/course/78 点击打开链接
打开课程的素材,只有两张图,即圆圈和针棒:
可以把Image文件夹整个拖到Project-Assets下,除此之外在这里再create几个folder:
Scene:存放场景,这个游戏就只有main场景一个
Prefabs:存放“预设”,在Unity3D的工程建设中,Prefabs(预设)是最非常用的一种资源类型,是一种可被重复使用的游戏对象。 特点1:它可以被置入多个场景中,也可以在一个场景中多次置入。 特点2:当你在一个场景中增加一个Prefabs,你就实例化了一个Prefabs。这里将要存入组合好的带圆圈的针
Scripts:存放C#脚本,处理游戏中组件的属性、操作等
(scripts下面添加的脚本)
大致思路:
设置一个mainCamara;拖入圆,调节大小,调为黑色,作为“被插针”的圆;添加Canvas下面的Text也就是UI->Text,记录分数;添加两个位置,起始位置和生成位置,分别表示屏幕上点击之前针的位置和屏幕外游戏中实例化针的位置;然后添加GameManager处理整个游戏的逻辑
对Circle的处理
调节成黑色;居中(x坐标为0);调节合适的大小,添加旋转的运动:也就是在Scripts下新建C#脚本RotateSelf,并拖拽到Circle的属性中;打开后在update()函数中添加旋转的代码:
void Update () { transform.Rotate(new Vector3(0, 0, -speed*Time.deltaTime)); }
这里的update函数在打开之初就有,每帧执行一次;
transform是unity组件,每个游戏对象都有组件,transition下常见的属性包括position(组件位置)、parent(组件父级)等用来描述物体的各种属性
transform.Rotate()为旋转物体的函数,参数为一个vector3也就是三维向量x,y,z,这里围绕Z轴旋转;而Time类包下面的一个重要重要的类变量deltaTime,它表示距上一次调用Update或FixedUpdate所用的时间。
对Text的处理:
需要调节好大小,这里的单位与圆的单位不一致,要比圆的单位大100倍,所以需要缩小,将其canvas一起缩小,最后拖拽到圆的中心,修改颜色,默认字符可以为0,居中显示
对针的处理:
这里处理完后整体拖入到Prefabs中做预设,就得在这一栏删掉了,窦泽屏幕上凭空多一个
可以先添加针的图片,再加入一个圆,但是把圆放在Pin的下面,这样整个Pin可以作为一个整体。调节针头的大小和颜色,然后移动的合适的位置做针头;针同样要调节到合适的大小和长度,然后可以把整个Pin拖到prefabs中作为一个预设,后面不断实例化和使其运动就可以了。其中这个circle针头的Add component中要添加Circle colider2D 和Rigidbody2D两个组件(搜索即可),是为了之后做碰撞检测。其中Circle colider2D中要勾选IsTrigger使得碰撞后还能继续做处理;Rigidbody2D里gravity要设为0,否则针头就会离针而去啦!
当然还得添加PinHead脚本(scripts文件夹下),才能具体的处理针头碰撞。另外在针头添加Tag PinHead以区分大圆做碰撞处理,需要Add tag之后再次选择,然后apply应用
在PinHead脚本中,添加下面的函数:
private void OnTriggerEnter2D(Collider2D collision) { if(collision.tag=="PinHead") GameObject.Find("GameManager").GetComponent<GameManager>().GameOver(); }
Gameobject是一个类型,所有的游戏物件都是这个类型的对象,是由component组成的;GameObject.find()通过名字来查找场景中的游戏对象,找到里面的GameManager组件下面的GameOver()方法;其中GameManager也是在Scripts下面添加的脚本文件,掌控整个游戏逻辑,里面会有涉及到游戏结束的函数,这里针头碰撞时要调用,可想而知,GameOver()是public的
整个针也有脚本Pin,需要控制针的运动;使其插上后一起跟圆旋转,Pin中的变量和函数,核心就两个函数
public float speed = 20;//针移动的速度 private bool isFly = false;//针是否在从起始点到圆的过程中 private bool isReach = false;//针是否实例化后到达了屏幕起始位置 private Transform startPoint;//屏幕上点击之前针的位置 private Transform circle;//获取组件大圆 private Vector3 targetCirclePosition;//由于不是插到圆心位置,需要另求目标位置 // Use this for initialization void Start () { startPoint = GameObject.Find("StartPosition").transform; circle = GameObject.Find("Circle").transform; targetCirclePosition = circle.position; targetCirclePosition.y -= 1.55f;//这里是理想碰撞下圆的y坐标和针的y坐标的差,也就是目标的插入位置 } /// <summary> /// 从起始点到插入,具体的移动见Update /// </summary> public void StartFly() { isFly = true; isReach = true; } // Update is called once per frame void Update () { if(isFly == false) { if(isReach==false) {//这一段是从屏幕外的生成位置,到屏幕上的开始位置,参数:当前位置,目标位置,速度 transform.position= Vector3.MoveTowards(transform.position, startPoint.position, speed * Time.deltaTime); if (Vector3.Distance(transform.position, startPoint.position) < 0.05f)//计算距离 isReach = true;//已到达起始位置 } } //从屏幕起始位置移动到圆 else { transform.position = Vector3.MoveTowards(transform.position, targetCirclePosition, speed * Time.deltaTime); if(Vector3.Distance(transform.position,targetCirclePosition)<0.05f) { transform.position = targetCirclePosition; transform.parent = circle;//这样就会和圆一起rotate isFly = false; } } }
GameManager
这里要拖入GameManager脚本,这个脚本就是和其他脚本的图标不一样QAQ
这个脚本主要是实例化针;响应鼠标点击事件;记录修改游戏得分;处理碰撞后游戏结束事件
其中实例化针:
//生成针:在spawnpoint位置实例化 void SpawnPin() { currentPin= GameObject.Instantiate(pinPrefab, spawnPoint.position, pinPrefab.transform.rotation).GetComponent<Pin>(); }
其中pinPrefab是public 的GameObject类型的对象,可以拖拽赋值为之前保存的预设;
开始函数,初始化获取参数,生成针:
void Start() { //获取具体位置 startPoint = GameObject.Find("StartPosition").transform; spawnPoint = GameObject.Find("SpawnPoint").transform; mainCamera = Camera.main;//主相机 SpawnPin(); animationSpeed = 3;//不在这里初始化可能会被初始化为0 }
Update随时响应鼠标点击事件:
// Update is called once per frame void Update() { if (isGameOver) return;//游戏结束不响应鼠标点击了 //鼠标点击事件 if(Input.GetMouseButtonDown(0)) { currentPin.StartFly();//从起始位置向上 score++;//更新分数 scoreText.text = score.ToString(); SpawnPin();//重新实例化针 } }
GameOver处理函数:
public void GameOver() { if (isGameOver) return; GameObject.Find("Circle").GetComponent<RotateSelf>().enabled = false;//停止旋转 StartCoroutine(GameOverAnimation());//游戏结束动画,自己另写一个协程 isGameOver = true; }
GameOver动画,将主相机的背景色渐变,大小渐变:
IEnumerator GameOverAnimation() { while(true) { //lerp()进行差值计算。背景色和大小都线性变化 mainCamera.backgroundColor = Color.Lerp(mainCamera.backgroundColor, Color.red, animationSpeed* Time.deltaTime); mainCamera.orthographicSize = Mathf.Lerp(mainCamera.orthographicSize, 4, animationSpeed * Time.deltaTime); Debug.Log(animationSpeed); if (Mathf.Abs(mainCamera.orthographicSize - 4) < 0.01f) break; yield return 0;//每次暂停一帧 } yield return new WaitForSeconds(0.2f);//动画暂停0.5秒 SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex);//重新加载场景 }unity协程是一个能暂停执行,暂停后立即返回,直到中断指令完成后继续执行的函数。
它类似一个子线程单独出来处理一些问题,性能开销较小,但是他在一个MonoBehaviour提供的主线程里只能有一个处于运行状态的协程。
效果如图:
这样就好啦
正常的游戏效果: