鼠标点选-玩家化身与游戏世界沟通的桥梁

37 篇文章 1 订阅
鼠标点选在游戏操作中是一个非常重要的概念,不论2d还是3d游戏,其映射给玩家的永远是一个2d平面,此时鼠标和游戏场景之间交互就存在一个问题,就是鼠标点选怎么映射到游戏中,所以就引出了射线原理这个概念,以摄像机为原点以鼠标在屏幕中位置为目标点,发射一条射线,获取这条射线终点的坐标就可以了。我们看下面这个简单的例子
using UnityEngine;
using System.Collections;

public class Script_ScreenPointToRay : MonoBehaviour {

	void Update() 
	{
		//鼠标按下左键
		if(Input.GetMouseButtonDown(0))
		{
			//创建一条射线以摄像机为原点
			Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
			RaycastHit hit;
			//射线碰撞到游戏地形时
			if (Physics.Raycast(ray, out hit))
			{
				//创建球体
				GameObject objCube = GameObject.CreatePrimitive(PrimitiveType.Sphere);
				objCube.transform.position = hit.point;

			}
		}
	}
}

我们看下一个简单的例子,在场景中,我们点选到一个区域,就会生成一个球体,如图

如果问这个有什么用,那试想一下,如果我们点选不是生成一个球体,而是场景中原有的一样物品,比如说一个矿石,我们点选之后,这个矿石Destroy,而我们背包里面add一个,这样就实现了物品的拾取,代码中使用Camera.main.ScreenPointToRay()方法有摄像机选中是发射一条射线,然后用Physics.Raycast()方法计算出改射线与地形之间的交点的位置。

如果觉得这个例子简单,我这里用一个国内u3d专家雨松写的一个例子更直观的说明鼠标点选的用处,

using UnityEngine;
using System.Collections;

public class Npc : MonoBehaviour {
	
	//游戏状态机
	
	//默认状态
	public const int TASK_STATE_DEFAULT = 0;
	//信息提示状态
	public const int TASK_STATE_OVER = 1;
	//打开任务窗口状态
	public const int TASK_STATE_OPEN = 2;
	//游戏状态
	public const int TASK_STATE_GAME = 3;
	//游戏完成状态
	public const int TASK_STATE_COMPLETE = 4;
	//游戏结束状态
	public const int TASK_STATE_FINSH = 5;
	
	//当前游戏状态
	public int gameState = 0;
	
	//脚本对象
	Script_Mouse_ray script = null;
	
	//任务窗口显示区域
	Rect windowRect  = new Rect(100,0,200,100);
	
	void Start()
	{
		//得到脚本对象
		script = GameObject.Find("/Main Camera").GetComponent<Script_Mouse_ray>();
		//在游戏存档中读取游戏状态数据
		gameState = PlayerPrefs.GetInt("gameState",TASK_STATE_DEFAULT);
	}

	void OnMouseDown() 
	{
		
		if(gameState == TASK_STATE_OVER)
		{
			//进入弹出任务窗口状态
			gameState = TASK_STATE_OPEN;
		}else
		if(gameState ==TASK_STATE_COMPLETE)
		{
			//进入任务完成状态
			gameState = TASK_STATE_FINSH;
		}
	}

	void OnMouseOver()
	{
		if(gameState == TASK_STATE_DEFAULT)
		{
			//进入提示信息状态
			gameState = TASK_STATE_OVER;
		}
	}
	
	void OnGUI()
	{
		//状态机的绘制
		switch(gameState)
		{
			case TASK_STATE_OVER:
				GUILayout.Box("点击Npc接一个任务");
			break;
			
			case TASK_STATE_OPEN:
				windowRect = GUILayout.Window (0, windowRect, AddWindow, "接受任务");
			break;
			case TASK_STATE_GAME:
				GUILayout.Box("目前收集到的宝石数量为:"+script.items);
			break;
			case TASK_STATE_COMPLETE:
				GUILayout.Box("已经收集到10颗宝石,请到NPC处交任务,宝石数量不再累加!");
			break;
			case TASK_STATE_FINSH:
				GUILayout.Box("任务完成");
				if(GUILayout.Button("重新开始"))
				{
					//重置游戏
					gameState = TASK_STATE_DEFAULT;
					script.items = 0;
					PlayerPrefs.SetInt("itemCount", script.items);
					PlayerPrefs.SetInt("gameState", gameState);
				}
			break;
		}

		
	}
	
	void AddWindow(int windowID)
	{
		
		GUILayout.Label("请在游戏中寻找20块红宝石");	
		 //开始一个水平布局
	 	GUILayout.BeginHorizontal ();
	 
	 	if (GUILayout.Button ("接受任务"))
	 	{
	 		gameState = TASK_STATE_GAME;
     	}

     	if (GUILayout.Button ("拒绝任务"))
	 	{
	 		gameState = TASK_STATE_DEFAULT;
     	}
      	//关闭水平布局
     	GUILayout.EndHorizontal ();
	}
}
using UnityEngine;
using System.Collections;

public class Script_Mouse_ray : MonoBehaviour {
	
	//红宝石数量
	public int items;
	//脚本对象
	private Npc npc = null;
	private int i=0;
	void Start()
	{
		//在游戏存档中读取上次保存游戏时红宝石的数量
		items =	PlayerPrefs.GetInt("itemCount",0);
		//获取脚本对象
		npc = GameObject.Find("/NPC").GetComponent<Npc>();
	
	}

	
	void Update() 
	{
		//是否游戏状态为游戏中
		if(npc.gameState == Npc.TASK_STATE_GAME)
		{
			//是否点击鼠标左键
			if(Input.GetMouseButtonDown(0))
			{
				//创建鼠标点击的射线
				Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
				RaycastHit hit;
				//鼠标是否选中游戏对象
				if (Physics.Raycast(ray, out hit))
				{
	                //得鼠标选中到游戏对象
					GameObject obj = hit.collider.gameObject;
				
					//是否为红宝石对象
					if(obj.name == "cube")
					{		
					    //红宝石数量加1
						items++;
						if(items>=10)
						{
							//当红宝石数量满足10,标示游戏完成
							npc.gameState = Npc.TASK_STATE_COMPLETE;
						}
						//销毁红宝石对象
						Destroy(obj);
						//保存红宝石数量以及游戏状态
						PlayerPrefs.SetInt("itemCount", items);
						PlayerPrefs.SetInt("gameState", npc.gameState);
					}else if(obj.name == "NPC"){
						i++;
						Debug.Log("选择Npc点选次数"+i);
					}
				}
			}
		}
	}	
}
Npc预制件

两个类的挂载,我们稍微分析下源码,Npc.cs里面定义了几个状态,通过void OnMouseDown() void OnMouseOver()两个方法来控制这几个状态的变化,
Script_Mouse_ray的update()方法中,通过发射射线来判断拾取方块,其实这个NPC类的void OnMouseDown() void OnMouseOver()方法其实也可以用射线来实现,参照Script_Mouse_ray中的else if(obj.name == "NPC")这一行,其实效果是一样的,我们看下效果没有接受任务之前状态是TASK_STATE_DEFAULT和TASK_STATE_OVER,所以点击Npc控制台没有反应,

接受任务之后因为状态进入了,TASK_STATE_GAME所以,我们看到点选Npc控制台会打印出点选的信息,这就是点选最常见的应用,除了这些还能做什么呢?

我开篇的时候说小游戏不加了,但感觉这篇点选的篇幅东西比较少,所以就准备加点格外的东东,用什么做的呢,还是点选,大家看下是什么东西

其实水果忍者就可以用射线选取的方式来做

我们看下代码

using UnityEngine;
using System.Collections;

public class StartController : MonoBehaviour {
	public GUISkin mySkin;
	private bool isSound1Button=false;
	private bool isSound2Button=true;
	private AudioSource sound;
	// Use this for initialization
	void Start () {
		sound=gameObject.GetComponent<AudioSource>();
	}
	
	// Update is called once per frame
	void Update () {
	
	}
	void OnGUI(){
		GUI.skin=mySkin;
		if(GUI.Button(new Rect(180,215,220,66),"",GUI.skin.GetStyle("PlayButton"))){
			Application.LoadLevel(1);
		}
		if(GUI.Button(new Rect(180,285,220,66),"",GUI.skin.GetStyle("MoreButton"))){
			//Application.OpenURL("www.sina.com");
		}

		if(GUI.Button(new Rect(180,355,220,66),"",GUI.skin.GetStyle("CreditButton"))){
			//Application.OpenURL("www.sina.com");
		}
		if(isSound1Button){
			Debug.Log("sound开启");
			if(GUI.Button(new Rect(25,430,37,31),"",GUI.skin.GetStyle("Sound1Button"))){
				sound.Play();
				isSound1Button=false;
				isSound2Button=true;
			}
		}
		if(isSound2Button){
			if(GUI.Button(new Rect(25,430,37,31),"",GUI.skin.GetStyle("Sound2Button"))){
				Debug.Log("sound关闭");
				sound.Stop();
				isSound1Button=true;
				isSound2Button=false;
			}
		}

	}
}

using UnityEngine;
using System.Collections;

public class KnifeRayController : MonoBehaviour {
	//public Color myColor;
	//private LineRenderer lineRenderer;
	public GameObject myRay;
	public AudioClip KnifeSound;
	public bool isHit=false;
	public Vector3 rayPosition;
	public bool isRay=false;
	public GameObject firstFruit;
	public GameObject secondFruit;

	public GameObject splash;
	public GameObject splashV;
	public GameObject splashH;
	//污渍
	private GameObject mySplash;
	private GameObject mySplashV;
	private GameObject mySplashH;


	private Vector3 firstPosition;
	private Vector3 secondPosition;
	//private Vector3 middlePosition;
	private GameObject rayGameObject;
	private bool isClicked=false;
	private GameObject myFirstFruit;
	private GameObject mySecondFruit;
	private float angle;
	// Use this for initialization
	void Start () {
//		lineRenderer=gameObject.AddComponent<LineRenderer>();
//		lineRenderer.material.color=myColor;
//		lineRenderer.SetWidth(0.1f,0.1f);

	}
	
	// Update is called once per frame
	void Update () {
		bool isMouseDown=Input.GetMouseButton(0);
		if(isHit){
			if(isMouseDown&&!isClicked){
				firstPosition=Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x,Input.mousePosition.y,1));
				//			lineRenderer.SetVertexCount(1);
				//			lineRenderer.enabled=true;
				//			lineRenderer.SetPosition(0,firstPosition);
				isClicked=true;
			}else if(isMouseDown){
				secondPosition=Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x,Input.mousePosition.y,1));
				//			lineRenderer.SetVertexCount(2);
				//			lineRenderer.SetPosition(1,secondPosition);
				
			}else if(Input.GetMouseButtonUp(0)){
				isRay=true;
				secondPosition=Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x,Input.mousePosition.y,1));
				if(secondPosition.x!=firstPosition.x){
						angle=(1.0f/3.14f)*180*Mathf.Atan((secondPosition.y-firstPosition.y)/(secondPosition.x-firstPosition.x));
				}else{
					angle=0;
				}
				if((angle<30&&angle>0)||(angle<0&&angle>-30)){
					//Debug.Log("splashh");
					mySplashH=Instantiate(splashH,transform.position+new Vector3(1.5f,0,0),Quaternion.identity)as GameObject;
					setColor(mySplashH);
					Destroy(mySplashH,1.0f);

				}else if((angle<90&&angle>60)||(angle<-60&&angle>-90)){
					//Debug.Log("splashv");
					mySplashV=Instantiate(splashV,transform.position+new Vector3(1.5f,0,0),Quaternion.identity)as GameObject;
					setColor(mySplashV);
					Destroy(mySplashV,1.0f);
					
				}else{
					//Debug.Log("splash");
					mySplash=Instantiate(splash,transform.position+new Vector3(1.5f,0,0),Quaternion.identity)as GameObject;
					setColor(mySplash);
					Destroy(mySplash,1.0f);
				
				}


				//rayGameObject=Instantiate(myRay,middlePosition,Quaternion.AngleAxis(angle*180/3.14f,Vector3.forward))as GameObject;
				rayGameObject=Instantiate(myRay,rayPosition,Quaternion.AngleAxis(angle*180/3.14f,Vector3.forward))as GameObject;
				//Destroy(
				myFirstFruit=Instantiate(firstFruit,transform.position,Quaternion.AngleAxis(angle*Random.Range(50,180)/3.14f,Vector3.forward))as GameObject;
				mySecondFruit=Instantiate(secondFruit,transform.position,Quaternion.AngleAxis(angle*Random.Range(80,150)/3.14f,Vector3.forward))as GameObject;
				if(Random.Range(1,10)>5.0f){
					myFirstFruit.rigidbody.velocity=new Vector2(5,10);
					mySecondFruit.rigidbody.velocity=new Vector2(-8,-10);
				}else{
					myFirstFruit.rigidbody.velocity=new Vector2(-5,10);
					mySecondFruit.rigidbody.velocity=new Vector2(8,-10);
				}
				Physics.gravity=new Vector3(0,-20,0);
				Destroy(myFirstFruit,2.0f);
				Destroy(mySecondFruit,2.0f);
				if(audio.isPlaying){
					audio.Stop();
				}
				PlaySound(KnifeSound);
				Destroy(rayGameObject,0.2f);
		
				isClicked=false;
				isHit=false;
				//			lineRenderer.SetVertexCount(2);
				//			lineRenderer.SetPosition(1,secondPosition);
				//middlePosition=(firstPosition+secondPosition)/2.0f;

				//float angle=Mathf.Atan((secondPosition.y-firstPosition.y)/(secondPosition.x-firstPosition.x));
				

			}

		}else{

			isRay=false;

		}

	

	}
	void PlaySound(AudioClip soundName){
		if(!audio.isPlaying){
			AudioSource.PlayClipAtPoint(soundName,new Vector3(0,0,-10));
		}

	}

	void setColor(GameObject myGameobject){
		Debug.Log("------------------");
		if(gameObject.name=="apple00(Clone)"){
			Debug.Log("apple00");
			myGameobject.gameObject.transform.renderer.material.color=Color.cyan;
		}else if(gameObject.name=="banana00(Clone)"){
			Debug.Log("banana00");
			myGameobject.gameObject.transform.renderer.material.color=Color.black;
		}else if(gameObject.name=="goldApple00(Clone)"){
			Debug.Log("goldApple00");
			myGameobject.gameObject.transform.renderer.material.color=Color.yellow;
		}
		else if(gameObject.name=="hamster00(Clone)"){
			Debug.Log("hamster00");
			myGameobject.gameObject.transform.renderer.material.color=Color.red;
			
		}
		else if(gameObject.gameObject.name=="kiwi00(Clone)"){
			Debug.Log("kiwi00");
			myGameobject.gameObject.transform.renderer.material.color=Color.gray;
			
		}else if(gameObject.name=="pear00(Clone)"){
			Debug.Log("pear00");
			myGameobject.gameObject.renderer.material.color=Color.white;
			
		}else if(gameObject.name=="waterMelon00(Clone)"){
			Debug.Log("waterMelon00");
			myGameobject.gameObject.transform.renderer.material.color=Color.green;
			
		}


	}


}

using UnityEngine;
using System.Collections;

public class HitByKnife : MonoBehaviour {
	public GameObject goldFruit;
	public AudioClip fallSound;
	public AudioClip hemsterSound;

	private GameObject myKnifeScript;
	private GameObject myGoldFruit;
	private bool isClicked=false;
	private RaycastHit hit;
	private Ray ray;
	// Use this for initialization
	void Start () {
	
	}
	
	// Update is called once per frame
	void Update () {
		bool isMouseDown=Input.GetMouseButton(0);
		if(!isClicked){
			//Debug.Log("+++++++++++++++++++++++");
			if(isMouseDown){
				ray=Camera.main.ScreenPointToRay(Input.mousePosition);
			
				if(collider.Raycast(ray,out hit,100.0f)){
					isClicked=true;
					transform.GetComponent<KnifeRayController>().isHit=true;
					transform.GetComponent<KnifeRayController>().rayPosition=hit.transform.position;

				}

			}
		}else if(transform.GetComponent<KnifeRayController>().isRay){
			Debug.Log("^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^");
			if(gameObject.name!="hamster00(Clone)"){
				if(gameObject.name=="goldApple00(Clone)"){
					Debug.Log("goldApple00(Clone)");
					myGoldFruit=Instantiate(goldFruit,transform.position,Quaternion.identity)as GameObject;
					DigitDisplay.score+=20;
					Destroy(myGoldFruit,1.0f);
				}else{
					DigitDisplay.score+=5;
				}
				gameObject.active=false;
				Destroy(gameObject,1.0f);
			}else{
				if(audio.isPlaying){
					audio.Stop();
				}
				PlaySound(hemsterSound);
				Debug.Log("^^^^^^^^^^^00000000000^^^^^^^^^^^^^^^");
				Destroy(gameObject);
				Application.LoadLevel(2);
			}

		}
		if(transform.position.y<=-7.1&&gameObject.name!="hamster00(Clone)"){
			if(audio.isPlaying){
				audio.Stop();
			}
			PlaySound(fallSound);
			DigitDisplay.life--;
			Destroy(gameObject);
		}

	}
	void PlaySound(AudioClip soundName){
		if(!audio.isPlaying){
			AudioSource.PlayClipAtPoint(soundName,new Vector3(0,0,-10));
		}
		
	}



}

using UnityEngine;
using System.Collections;

public class DigitDisplay : MonoBehaviour {
	//生命
	public Texture liftTexture;
	public Texture liftTextureX;
	public Texture[] lifeNumber;
	public static int life=3;
	//计分
	public static int score=0;
	public Texture[] scoreNumber;
	//倒计时
	float myTime;
	public Texture[] timeNumber;
	public int leftTime=100;
	public int Level=1;
	// Use this for initialization
	void Start () {
	
	}
	
	// Update is called once per frame
	void Update () {
		if(life<=0){
			Application.LoadLevel(2);
		}
		myTime+=Time.deltaTime;

		if(myTime>1){

			if(leftTime>0){
				leftTime--;
			}else{
				Application.LoadLevel(2);
			}
			myTime=0;
		}
		if(score>50&&Level==1){
			Application.LoadLevel(3);
		}

	}
	void OnGUI(){
		GUI.DrawTexture(new Rect(10,10,20,30),liftTexture);
		GUI.DrawTexture(new Rect(30,10,35,35),liftTextureX);
		for(int i=0;i<life.ToString().Length;i++){
			GUI.DrawTexture(new Rect(60+i*20,20,20,20),lifeNumber[System.Int32.Parse((life.ToString())[i].ToString())]);
		}
		for(int i=0;i<leftTime.ToString().Length;i++){
			GUI.DrawTexture(new Rect(300+i*32,20,32,32),timeNumber[System.Int32.Parse((leftTime.ToString())[i].ToString())]);
		}
		//GUI.Label(new Rect(360,10,80,20),"Score:");
		for(int i=0;i<score.ToString().Length;i++){
			GUI.DrawTexture(new Rect(500+i*32,20,40,40),scoreNumber[System.Int32.Parse((score.ToString())[i].ToString())]);
		}
	}
}

using UnityEngine;
using System.Collections;

public class fruitCreate : MonoBehaviour {
	public GameObject[] fruit=new GameObject[2];
	public int index;
	//ctor3
	private GameObject myApple;
	// Use this for initialization
	void Start () {
		Physics.gravity=new Vector3(0,-20,0);
		InvokeRepeating("Move",0,3.2f);
	}
	
	// Update is called once per frame
	void Update () {
	
	}
	void Move(){
		float x=Random.Range(-7.0f,7.0f);
		index=Random.Range(0,7);
		myApple=Instantiate(fruit[index],new Vector3(x,-7,-9),Quaternion.identity)as GameObject;
		if(x>0){
			myApple.transform.rigidbody.velocity=new Vector3(Random.Range(-5,0),21.0f,0.0f);
		}else{
			myApple.transform.rigidbody.velocity=new Vector3(Random.Range(0,5),21.0f,0.0f);
		}
//例子
//var temp = transform.position;
//temp.y=0.5f;
//transform.position = temp;
		Vector3 temp=myApple.transform.localEulerAngles;
		temp.z=Random.Range(-120.0f,120.0f);
		//myApple.transform.localEulerAngles=temp;
      // myApple.transform.localEulerAngles.z=Random.Range(-120.0f,120.0f);

	}


}

using UnityEngine;
using System.Collections;

public class Level1soundGUI : MonoBehaviour {
	public GUISkin mySkin;
	private bool isSound1Button=false;
	private bool isSound2Button=true;
	private AudioSource sound;
	// Use this for initialization
	void Start () {
		sound=gameObject.GetComponent<AudioSource>();
	}
	
	// Update is called once per frame
	void Update () {
	

	}
	void OnGUI(){
		GUI.skin=mySkin;
		if(isSound1Button){
			//Debug.Log("sound开启");
			if(GUI.Button(new Rect(12,430,21,31),"",GUI.skin.GetStyle("Sound1Button"))){
				sound.Play();
				isSound1Button=false;
				isSound2Button=true;
			}
		}
		if(isSound2Button){
			if(GUI.Button(new Rect(12,430,21,31),"",GUI.skin.GetStyle("Sound2Button"))){
				//Debug.Log("sound关闭");
				sound.Stop();
				isSound1Button=true;
				isSound2Button=false;
			}
		}

	}
}
这个山寨的小游戏,其实只有6个类,最重要的类HitByKnife.cs和KnifeRayController.cs。也就是产生小刀刀锋的方法就是发射一条射线点选的过程
具体挂载方法如图


好了本节就到这吧,下节我们继续讨论仙剑Demo的制作,怎么感觉不想是做仙剑了。。。!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值