全景图+简单UI交互

实验记录。(4.2Update-finish)

全景图来自网络素材,原本这一步实现图片的拼接也是可以的,但是觉得那样过于无关了点,网上随便扒了张图下来。(可能会换)

利用unity创造模型,实现球内贴图。一开始的贴图是在球面即外部,需要使用着色器改成内部

参考博客: https://blog.csdn.net/wuyt2008/article/details/5431347

Shader "InsideVisible" {
Properties {
     _MainTex ("Base (RGB)", 2D) = "white" {}
 }
SubShader {
     Tags { "RenderType"="Opaque" }
     Cull front    // ADDED BY BERNIE, TO FLIP THE SURFACES
     LOD 100
Pass {  
    CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag

        #include "UnityCG.cginc"

        struct appdata_t {
            float4 vertex : POSITION;
            float2 texcoord : TEXCOORD0;
        };

        struct v2f {
            float4 vertex : SV_POSITION;
            half2 texcoord : TEXCOORD0;
        };

        sampler2D _MainTex;
        float4 _MainTex_ST;

        v2f vert (appdata_t v)
        {
            v2f o;
            o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
            // ADDED BY BERNIE:
            v.texcoord.x = 1 - v.texcoord.x;                
            o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
            return o;
        }

        fixed4 frag (v2f i) : SV_Target
        {
            fixed4 col = tex2D(_MainTex, i.texcoord);
            return col;
        }
    ENDCG
}
}}
实现变成内部的“翻转”效果

这种情况下在scene里固然可以通过alt+左键的方式环绕浏览全景图,但是game模式下相机的视角是固定的 需要通过编写脚本将相机和鼠标或者键盘的操纵绑定起来。此处因为我们希望鼠标还有后续的ui操作互动,所以不能使用直接看不见鼠标->鼠标直接引导方向旋转的方式。

不过找到了一份很不错的直接引导旋转的代码,先存一下

using UnityEngine;
 
using System.Collections;
using System.Collections.Generic;
 
public class SmoothMouseLook : MonoBehaviour {
 
    public float sensitivity = 4.0f;
	[HideInInspector]
	public float sensitivityAmt = 4.0f;//actual sensitivity modified by IronSights Script
 
    private float minimumX = -360f;
    private float maximumX = 360f;
 
    private float minimumY = -85f;
    private float maximumY = 85f;
	[HideInInspector]
    public float rotationX = 0.0f;
	[HideInInspector]
    public float rotationY = 0.0f;
	[HideInInspector]
    public float inputY = 0.0f;
   
	public float smoothSpeed = 0.35f;
	
	private Quaternion originalRotation;
	private Transform myTransform;
	[HideInInspector]
	public float recoilX;//non recovering recoil amount managed by WeaponKick function of WeaponBehavior.cs
	[HideInInspector]
	public float recoilY;//non recovering recoil amount managed by WeaponKick function of WeaponBehavior.cs
	
	void Start(){         
        if (GetComponent<Rigidbody>()){GetComponent<Rigidbody>().freezeRotation = true;}
		
		myTransform = transform;//cache transform for efficiency
		
		originalRotation = myTransform.localRotation;
		//sync the initial rotation of the main camera to the y rotation set in editor
		Vector3 tempRotation = new Vector3(0,Camera.main.transform.eulerAngles.y,0);
		originalRotation.eulerAngles = tempRotation;
		
		sensitivityAmt = sensitivity;//initialize sensitivity amount from var set by player
		
		// Hide the cursor
		Cursor.visible = false;
    }
 
    void Update(){
		
		if(Time.timeScale > 0 && Time.deltaTime > 0){//allow pausing by setting timescale to 0
			
			//Hide the cursor
			Screen.lockCursor = true;
			Cursor.visible = false;
			
			// Read the mouse input axis
			rotationX += Input.GetAxisRaw("Mouse X") * sensitivityAmt * Time.timeScale;//lower sensitivity at slower time settings
			rotationY += Input.GetAxisRaw("Mouse Y") * sensitivityAmt * Time.timeScale;
			
			//reset vertical recoilY value if it would exceed maximumY amount 
			if(maximumY - Input.GetAxisRaw("Mouse Y") * sensitivityAmt * Time.timeScale < recoilY){
				rotationY += recoilY;
				recoilY = 0.0f;	
			}
			//reset horizontal recoilX value if it would exceed maximumX amount 
			if(maximumX - Input.GetAxisRaw("Mouse X") * sensitivityAmt * Time.timeScale < recoilX){
				rotationX += recoilX;
				recoilX = 0.0f;	
			}
			 
			rotationX = ClampAngle (rotationX, minimumX, maximumX);
			rotationY = ClampAngle (rotationY, minimumY - recoilY, maximumY - recoilY);
			
			inputY = rotationY + recoilY;//set public inputY value for use in other scripts
			 
			Quaternion xQuaternion = Quaternion.AngleAxis (rotationX + recoilX, Vector3.up);
			Quaternion yQuaternion = Quaternion.AngleAxis (rotationY + recoilY, -Vector3.right);
			
			//smooth the mouse input
			myTransform.rotation = Quaternion.Slerp(myTransform.rotation , originalRotation * xQuaternion * yQuaternion, smoothSpeed * Time.smoothDeltaTime * 60 / Time.timeScale);
			//lock mouselook roll to prevent gun rotating with fast mouse movements
			myTransform.rotation = Quaternion.Euler(myTransform.rotation.eulerAngles.x, myTransform.rotation.eulerAngles.y, 0.0f);
			
		}else{
			//Show the cursor
			Screen.lockCursor = false;
			Cursor.visible = true;	
		}
		
    }
   
	//function used to limit angles
    public static float ClampAngle (float angle, float min, float max){
        angle = angle % 360;
        if((angle >= -360F) && (angle <= 360F)){
            if(angle < -360F){
                angle += 360F;
            }
            if(angle > 360F){
                angle -= 360F;
            }         
        }
        return Mathf.Clamp (angle, min, max);
    }
	
}

这份代码甚至考虑到了旋转的微弱惯性,真是有意思。

下面是直接鼠标点击拖动(game窗口中有鼠标)实现旋转:

using UnityEngine;
using System.Collections;
using UnityEngine.UI;
 
public class Test01 : MonoBehaviour
{
    public float moveSpeed = 1;//物体旋转速度  
    public GameObject target;
 
    private Vector2 oldPosition;
    private Vector2 oldPosition1;
    private Vector2 oldPosition2;
 
 
    private float distance = 0;
    private bool flag = false;
    //摄像头的位置
    private float x = 0f;
    private float y = 0f;
    //左右滑动移动速度
    public float xSpeed = 250f;
    public float ySpeed = 120f;
    //缩放限制系数
    public float yMinLimit = -360;
    public float yMaxLimit = 360;
    //是否旋转
    private bool isRotate = true;
    //计数器
    private float count = 0;
 
    public static Test01 _instance;
    //初始化游戏信息设置
    void Start()
    {
        _instance = this;
        Vector3 angles = transform.eulerAngles;
        x = angles.y;
        y = angles.x;
        if (GetComponent< Rigidbody >())
            GetComponent< Rigidbody >().freezeRotation = true;
    }
 
    
 
    // Update is called once per frame  
    void Update()
    {
 
        if (isRotate)
        {
 
            target.transform.Rotate(Vector3.down, Time.deltaTime * moveSpeed,Space.World);
            
        }
        if (!isRotate)
        {
            count += Time.deltaTime;
            if (count > 5)
            {
                count = 0;
                isRotate = true;
            }
        }
 
            //触摸类型为移动触摸
            if (Input.GetMouseButton(0))
            {
                //根据触摸点计算X与Y位置
                x += Input.GetAxis("Mouse X") * xSpeed *Time.deltaTime;
                y -= Input.GetAxis("Mouse Y") * ySpeed * Time.deltaTime;
            isRotate = false;
            }
            //判断鼠标滑轮是否输入
        float temp = Input.GetAxis("Mouse ScrollWheel");
        if (temp!=0)
        {
            if (temp>0)
            {
                // 这里的数据是根据我项目中的模型而调节的,大家可以自己任意修改
                if (distance > -15)
                {
                    distance -= 0.5f;
                }
            }
            if (temp<0)
            {
                // 这里的数据是根据我项目中的模型而调节的,大家可以自己任意修改
                if (distance < 20)
                {
                    distance += 0.5f;
                }
            }
        }
 
    }
 
    //计算距离,判断放大还是缩小。放大返回true,缩小返回false  
    bool IsEnlarge(Vector2 oP1, Vector2 oP2, Vector2 nP1, Vector2 nP2)
    {
        //old distance  
        float oldDistance = Mathf.Sqrt((oP1.x - oP2.x) * (oP1.x - oP2.x) + (oP1.y - oP2.y) * (oP1.y - oP2.y));
        //new distance  
        float newDistance = Mathf.Sqrt((nP1.x - nP2.x) * (nP1.x - nP2.x) + (nP1.y - nP2.y) * (nP1.y - nP2.y));
 
        if (oldDistance < newDistance)
        {
            //zoom+  
            return true;
        }
        else
        {
            //zoom-  
            return false;
        }
    }
 
    //每帧执行,在Update后  
    void LateUpdate()
    {
        if (target)
        {
            //重置摄像机的位置
            y = ClampAngle(y, yMinLimit, yMaxLimit);
            var rotation = Quaternion.Euler(y, x, 0);
            var position = rotation *(new Vector3(0.0f, 0.0f, -distance)) + target.transform.position;
 
            transform.rotation = rotation;
            transform.position = position;
        }
    }
    float ClampAngle(float angle,float min,float max) {
        if (angle < -360)
            angle += 360;
        if (angle > 360)
            angle -= 360;
        return Mathf.Clamp(angle, min, max);
 
    }
   
}

将这份脚本绑到main camera后需要手动将main camera拖进在inspector中的脚本参数设置【固定旋转中心

在可以确保基本的全景图浏览后,开始考虑我们的ui要怎么做了。

懒得做场景跳转,想来想去还是模仿了前辈作业“逃出二次元”,其实就只是场景中有物体、点击物体触发事件。

游戏规则:进入游戏场景,从场景中通过点击揪出六只企鹅就结束。

(嗨呀这张原图拼的效果真差……扭曲的厉害)
图上为鼠标点击左下角企鹅后的情况。跟随鼠标出现文字,点击后左上角出现企鹅

点击某只企鹅后左上角出现的企鹅是固定的(直接用多个脚本绑到一起),其实可以通过一个长一点的脚本直接实现绑定多个企鹅触发器,但我偷懒了(。)

找到所有企鹅以后中间跳出红色提示字符,请自行通过关闭游戏窗口/任务管理器退出(是的我又偷懒不写button了)

好,效果讲完了,这是技术部分。

由于没有找到如何实现单张图片(非canvasUI)的3D物体建模(如果是厚度为0的cube,会出现透明底图片翻转出现的情况),所以选择了把企鹅直接拼到全景图中的方法。

(天花板上那只在贴图后被惨烈的拉成了一长条【。】)

目标是点击/移动到企鹅位置时触发文字和图片效果。所以为每个企鹅添加cube+box collider

位置有个大体范围就行,反正纵轴只是影响边角时的选取情况。

然后是鼠标移动/点击时的代码内容

using UnityEngine;
using System.Collections;
 
public class Cube0 : MonoBehaviour
{
    //    public Transform cube;
    bool isShowTip;
    public bool WindowShow = false;
    public GameObject qe1;
    //    // Use this for initialization
    void Start()
    {
        isShowTip = false;
        WindowShow = false;
    }
    void OnMouseEnter()
    {
        isShowTip = true;
        //Debug.Log (cube.name);//可以得到物体的名字
 
    }
    void OnMouseExit()
    {
        isShowTip = false;
    }
    void OnGUI()
    {
        if (isShowTip)
        {
            GUIStyle style1= new GUIStyle();
            style1.fontSize = 20;
            style1.normal.textColor = Color.white;
            GUI.Label(new Rect(Input.mousePosition.x, Screen.height - Input.mousePosition.y, 450, 100),"!我还没来得及藏呢!", style1);
 
        }
     //   if (WindowShow)
      //      GUI.Window(0, new Rect(30, 30, 200, 100), MyWindow, "Cube");
    }
 
    //对话框函数
    void MyWindow(int WindowID)
    {
        GUILayout.Label("你想写入的内容");
    }
    //鼠标点击事件
    void OnMouseDown()
    {
        Debug.Log("find");
       //  Debug.Log("show");
        if (WindowShow)
            WindowShow = false;
        else
            WindowShow = true;
        qe1.SetActive(true);
    }
}

里面有一些无用代码,为了以后可能用到所以保留。(参考博客:

https://blog.csdn.net/hanguangfei/article/details/78094214

无用☞点击cube在界面左上角出现一个文字提示框。反复点击反复开关

添加了☞绑定image的开关

接下来是找到所有企鹅以后跳出中心文字提示。

实现方法:因为之前把脚本拆开来写,就没有全局的统计揪了多少只企鹅,所以通过检测image的显示情况(所有图标都出现==成功),引入函数GameObject.activeSelf

(附带:Unity GameObject.activeSelf, activeInHierarchy, SetActive和SetActiveRecursively区别 https://blog.csdn.net/qq_17758883/article/details/80175215

一开始试图把检测脚本绑到显示成功的文本,但是无论怎样都无法成功实现。猜测可能在游戏开始时是false,所以不执行里面的update。在canvas左下角添加一个看不见的button(alpha==255),在update里添加代码:

            if (qe1.activeSelf && qe2.activeSelf && qe3.activeSelf && qe4.activeSelf && qe5.activeSelf && qe6.activeSelf)
            {
                findall.SetActive(true);
          }

好,主要的说完了,剩下的是零零散散:

·所有UI都可以直接添加button组件,实现点击效果。

·已经有了触发器的cube,不必搞什么相机射线检测一类复杂脚本,直接写void OnMouseDown()写点击事件

·Input.GetAxis(“Mouse ScrollWheel”);检测滚轮输入情况,本实验中注释掉了因为没啥用,否则要注意数值情况要不然一滚就飞出天外。

·Fin…..?

发表评论

电子邮件地址不会被公开。 必填项已用*标注