2017-03-07 00:22:50 marvelgl 阅读数 412

第一次用Unity 3D写游戏,做了个井字棋。
这里写图片描述
由于不熟悉onGUI()和C#,参考了师兄的笔记。

代码实际上很简单,逻辑也简单,主要是通过这个小游戏来熟悉一下Unity 3D的一些基本操作实现,比如OnGUI()的原理,实际上这里面的Button并不是点击触发的效果,而是每一帧都在发生改变,因此这里Button实际上是不断重叠制造的吧。(显然变深色了),我感觉这样的做法不太好,但暂时不知道怎么去更好实现,就先这样了。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TicTacToe: MonoBehaviour {
    int turn = 1;
    int [,]state = new int[3,3];
    void Start () {
        Reset();
    }

    void Update () { }

    void OnGUI() {
        if (GUI.Button(new Rect(0,0,150,50), "Start Game")) {
            Reset();
        }
        int current = Check();
        if (current == 1) GUI.Label(new Rect(50,55,50,50),"O wins!");
        if (current == 2) GUI.Label(new Rect(50,55,50,50),"X wins!");
        if (current == 3) GUI.Label(new Rect(50,55,50,50),"Tied!");
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (state[i, j] == 1) {
                    GUI.Button(new Rect(0+50*i,80+50*j,50,50), "O");
                } else if (state[i, j] == 2) {
                    GUI.Button(new Rect(0+50*i,80+50*j,50,50), "X");
                } 
                if (GUI.Button(new Rect(0+50*i,80+50*j,50,50), "")) {
                    if (current == 0) {
                        if (turn == 1) state[i, j] = 1;
                        if (turn == -1) state[i, j] = 2;
                        turn *= -1;
                    }
                }
            }
        }
    }

    int Check() {
        for (int i = 0; i < 3; i++) {
            if (state[i, 0] == state[i, 1] && state[i, 0] == state[i, 2] && state[i, 0] != 0) {
                return state[i, 0];
            }
        }

        for (int j = 0; j < 3; j++) {
            if (state[0, j] == state[1, j] && state[2, j] == state[0, j] && state[0, j] != 0) {
                return state[0, j];
            }
        }

        if (state[1, 1] != 0 && state[0, 0] == state[1, 1] && state[2, 2] == state[1, 1]) {
            return state[1, 1];
        }

        if (state[1, 1] != 0 && state[0, 2] == state[1, 1] && state[2, 0] == state[1, 1]) {
            return state[1, 1];
        }

        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (state[i, j] == 0) return 0;
            }
        }

        return 3;
    }

    void Reset() {
        turn = 1;
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                state[i, j] = 0;
            }
        }
    }
}
2018-12-28 23:22:26 lydcsdn 阅读数 353

需要实现的功能

今天在做项目时,要实现鼠标点击选取功能。因为搭建的是一个开放的世界,场景中大部分能看到的东西都需要能被选取,再加上是3D场景,所以存在大量的物体重叠,这时鼠标点击所选取到的物体应该是当前屏幕上被点击位置相对最靠前的物体,所以尝试用射线检测碰撞体来实现,射线最先碰到的物体,应该就是点击位置最靠前的物体。

国际惯例代码如下:

// An highlighted block
//建立主摄像机到鼠标点击位置的射线
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
//建立碰撞信息数组
RaycastHit[] hitInfo;
//发出射线             
hitInfo = Physics.RaycastAll(ray, 1000);
//选取数组中第一个元素作为被鼠标点选的物体
if(hitInfo>0)
	GameObject GetObj=hitInfo[0].collider.gameobject;

遇到的问题

因本人智商捉急,自认为得到的碰撞信息数组:
RaycastHit[] hitInfo;
的元素排列顺序就是射线从摄像机位置开始,依次从近到远碰撞到的物体的顺序。但经过一系列测试后发现得到的数组元素顺序有其他规则,并不是按照射线碰撞顺序来的。

解决问题

如果要得到最靠前的物体,需要读取碰撞信息里的距离信息:hitInfo[i].distance
代码如下:

//设置一个绝对大于摄像机到所有物体距离的距离值
float minidis = 10000;	
GameObject GetObj=new GameObject();
for (int i=0;i< hitInfo.Length;i++)
	{
		if (hitInfo[i].distance < minidis)
        {
        	GetObj = hitInfo[i].collider.gameobject;
            minidis = hitInfo[i].distance;
        }
    }        
2020-03-23 11:01:05 qq_37760273 阅读数 13

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class ClickMouse : MonoBehaviour
{
    private int _index;
    private GraphicRaycaster _raycaster;
    // Start is called before the first frame update
    void Start()
    {
        _raycaster = FindObjectOfType<GraphicRaycaster>();
    }


    private void OnMouseDown()
    {
        //ChangeColor();
        if (!IsUI())
        {
            ChangeColor();
        }
    }

    void ChangeColor()
    {
        if (_index == 0)
        {
            GetComponent<MeshRenderer>().material.SetColor("_Color", Color.black);
        }
        else
        {
            GetComponent<MeshRenderer>().material.SetColor("_Color", Color.white);
        }
        _index = _index == 0 ? 1 : 0;
    }

    private bool IsUI()
    {
        PointerEventData data = new PointerEventData(EventSystem.current);
        data.pressPosition = Input.mousePosition;
        data.position = Input.mousePosition;

        List<RaycastResult> results = new List<RaycastResult>();
        _raycaster.Raycast(data, results);
        return results.Count > 0;
    }
}

2015-05-27 10:26:50 hasion 阅读数 3597

在Unity5.0之后,NGUI的导入多少回带入一些错误,很多都是组件的问题,在unity4.6的时候,NGUI还是可以用的,不过现在ngui就显得越来越多余了,更多的人会去选择UGUI去搭建自己的UI。

 在新的UI系统下,因为UI和3D物体都是在一个相机下渲染的,这样就出现UI上的点击和3D物体的点击冲突问题。当UI和3D问题重叠是,如何去判别呢。这边集合自己搜集的资料以及自己的想法。整理如下:

新版UI下,去掉了UI组件的碰撞,所以用射线检测 显然是行不通的。看UGUI的API可以发现,EventSystem有一个方法可以实现是否点击到UI,
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class point : MonoBehaviour {

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {

            if (EventSystem.current.IsPointerOverGameObject())
            {
                print("在UI上");
            }
            else
            {
                print("不在UI上");
            }
        }
    }

}

这个方法测试过 是可以实现的。不过只是检测是否点击了UI,在很多情况下,这样的功能是不够用的,而且网上有的人测试这个方法还有bug,所以果断放弃这个方法。

后来后人想到一个方案  就是用PhysicsRaycaster+EventSystem 实现射线点击。方法如下:
1、先给相机添加PhysicsRaycaster组件:


这边可以选择相应的层,非常的方便好用。
2、在一个UI和3D的共存的场景中,添加相应的想要对3D物体操作的脚本到3D物体上。EventSystem 所有能在UI上添加的事件都可以用在3D物体,具体有哪些方法可以查看官方api。下面我以拖拽3D物体为例,代码比较简单,就不注释了,

using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class ObjCheck : MonoBehaviour ,IBeginDragHandler,IDragHandler
{
    public void OnBeginDrag(PointerEventData eventData)
    {
        print(eventData.pointerDrag.name);
    }

    public void OnDrag(PointerEventData eventData)
    {
        Vector3 worldPos;
        Vector3 screenpos = Camera.main.WorldToScreenPoint(transform.position);

        worldPos = Camera.main.ScreenToWorldPoint(new Vector3(Input.mousePosition.x, Input.mousePosition.y, screenpos.z));

        transform.position = worldPos;



    }

}

第二个方法就比较的实用,而且可以对3D物体实现很多的操作。而且对3D的操作也不影响UI的点击。我觉得是个很不错的方法。







2016-11-25 16:00:54 mseol 阅读数 1747

Unity中当使用了UnityEngine.Ray或Input.Touch,会遇到UI层和3D物体层重叠的问题。通过UnityEngine.EventSystems,可以检测UI点击,屏蔽3D Collider的干扰。

基础方法

 

using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;

public class UIBlock : MonoBehaviour
{
	void Update ()
    {
        if (EventSystem.current.IsPointerOverGameObject())
        {
            Debug.Log("是UI");
            return;
        }

        if (Input.GetMouseButton(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            RaycastHit hit;

            if (Physics.Raycast(ray, out hit))
            {
                if (hit.transform.gameObject.tag != "Untagged")
                {
                    Debug.Log(hit.transform.name);
                }
            }
        }
	}
}

 

扩展方法

EventSystem.current.IsPointerOverGameObject() 括号内填写参数fingerId,不填默认指向左键。这个方法用于在移动端上处理以上问题。

 

using UnityEngine;
using System.Collections;
using UnityEngine.EventSystems;

public class TouchExample : MonoBehaviour
{
    void Update()
    {
        // Check if there is a touch
        if (Input.touchCount > 0 && Input.GetTouch(0).phase == TouchPhase.Began)
        {
            // Check if finger is over a UI element
            if (EventSystem.current.IsPointerOverGameObject(Input.GetTouch(0).fingerId))
            {
                Debug.Log("Touched the UI");
            }
        }
    }
}

 

这样就能保证我们点击的是UI了。


 

没有更多推荐了,返回首页