周记目录
本周任务
- 基于增强图片技术实现3D物体的拖拽
- 将已做好的3D模型导入Unity并测试
AR中实现3D物体的拖拽(伪3D)
因为在手机的 2D 屏幕上实现对 3D 物体的拖拽操作会非常麻烦,结合项目的需要,所以做成伪 3D 的效果,即操作是在一个固定平面上的,却在 3D 空间中观察,类似于固定的第三人称视角游戏,或者类似于在黑板上画图,我们可以走动观察它。如果要做成 3D 操作也有先例可借鉴,如 IOS 中的 Paint Space AR ,一款 AR 画图的软件。
做法思路
固定一个竖直的平面,这个平面看作实验操作平面。利用Unity中的射线 Ray 与 Raycast ,点击屏幕时从摄像头发出一条只会碰撞到化学实验仪器的射线,碰到仪器后再重新发射一条只会碰到实验操作平面的射线,将第一次射线碰到的仪器的位置移动到第二条射线碰到实验操作平台的碰撞位置。
下面开始说做法:
1. 添加 Collider
ARcore 示例中的模型是没有加碰撞器的,这一点我竟然没有发现……只有加碰撞器的物体才能被 Ray 碰撞到。
ARcore AugmentedImage 示例项目中模型文件可以按照下图方法查找:
对相框角添加 Collider 如下图,我对单个相框角添加的是这个碰撞器。
然后,调整碰撞器的中心和大小。初始的大小是刚刚好包裹着相框角模型的,但是实际运行的时候相框角模型较小,点中和拖动都会有影响,因为实际点中碰撞器的点和碰撞器中心的偏差是能够拖动的原因,太小的话拖动快一点手指就飞出碰撞体了。所以我调大了一圈,这样的话拖动更方便。参数与大小如下:
2. 添加 Plane
在 AugmentedImageVisualizer 预制体下添加一个 Plane ,这样此平面就能跟着识别图一起移动。这个平面是实验操作平面。将此平面设为竖直方向,参数如下:
此时平面很大,下周将此平面调成到合适大小。
3. 设置 Layer
Layer 的作用是控制射线 Ray 能碰到的图层。我们把4个相框角设置成 object ,实验操作平面设置为 plane (这两个都是自己添加的 Layer ),如下两个图。
4. 脚本分析与更改
这周先做效果,就直接更改了 AugmentedImageVisualizer 脚本。下周重新写一个挂载到 EventSystem 下。想要实践的同学直接看下周这部分的总结!!!!
下面是更改后的 AugmentedImageVisualizer 代码。注意 Physics.Raycast(ray, out hitInfoPlane,2, mask2.value) 函数,第三个和第四个参数都是数值型,所以第三个参数不能省略,否则出现重载问题。
//-----------------------------------------------------------------------
// <copyright file="AugmentedImageVisualizer.cs" company="Google">
//
// Copyright 2018 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// </copyright>
//-----------------------------------------------------------------------
namespace GoogleARCore.Examples.AugmentedImage
{
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using GoogleARCore;
using GoogleARCoreInternal;
using UnityEngine;
/// <summary>
/// Uses 4 frame corner objects to visualize an AugmentedImage.
/// </summary>
public class AugmentedImageVisualizer : MonoBehaviour
{
/// <summary>
/// The AugmentedImage to visualize.
/// </summary>
public AugmentedImage Image;
/// <summary>
/// A model for the lower left corner of the frame to place when an image is detected.
/// </summary>
public GameObject FrameLowerLeft;
/// <summary>
/// A model for the lower right corner of the frame to place when an image is detected.
/// </summary>
public GameObject FrameLowerRight;
/// <summary>
/// A model for the upper left corner of the frame to place when an image is detected.
/// </summary>
public GameObject FrameUpperLeft;
/// <summary>
/// A model for the upper right corner of the frame to place when an image is detected.
/// </summary>
public GameObject FrameUpperRight;
/// <summary>
/// The Unity Update method.
/// </summary>
///
private Camera cam;//发射射线的摄像机
private GameObject go;//射线碰撞的物体
public static string btnName;//射线碰撞物体的名字
private void Start()
{
cam = Camera.current;
Debug.Log("start");
}
public void Update()
{
if (Image == null || Image.TrackingState != TrackingState.Tracking)
{
//如果没有找到需要识别的图片,则把四个相框角都隐藏掉
FrameLowerLeft.SetActive(false);
FrameLowerRight.SetActive(false);
FrameUpperLeft.SetActive(false);
FrameUpperRight.SetActive(false);
return;
}
//找到识别图,把四个相框的位置固定到应该显示的位置
//注意用的 localPosition ,是相对于 AugmentedImageVisualizer 的局部位置
float halfWidth = Image.ExtentX / 2;
float halfHeight = Image.ExtentZ / 2;
FrameLowerLeft.transform.localPosition =
(halfWidth * Vector3.left) + (halfHeight * Vector3.back);
FrameLowerRight.transform.localPosition =
(halfWidth * Vector3.right) + (halfHeight * Vector3.back);
FrameUpperLeft.transform.localPosition =
(halfWidth * Vector3.left) + (halfHeight * Vector3.forward);
FrameUpperRight.transform.localPosition =
(halfWidth * Vector3.right) + (halfHeight * Vector3.forward);
FrameLowerLeft.SetActive(true);
FrameLowerRight.SetActive(true);
FrameUpperLeft.SetActive(true);
FrameUpperRight.SetActive(true);
if (Input.GetMouseButton(0)) //监测点击事件 鼠标与手机屏幕都可
{
//以摄像头为起点的第一条射线
Ray ray = cam.ScreenPointToRay(Input.touches[0].position);
RaycastHit hitInfo;
//制作 LayerMask ,只有 object 的 Layer ,用于下面射线判定
LayerMask mask1 = 1 << LayerMask.NameToLayer("object");
//如果射线在2M范围内碰到 mask1 的物体,进入此分支
if (Physics.Raycast(ray, out hitInfo,2, mask1.value))
{
Debug.Log("Object 1 " + hitInfo.collider.gameObject.name+" "+hitInfo.collider.gameObject.layer);
go = hitInfo.collider.gameObject;
RaycastHit hitInfoPlane;
//制作 LayerMask ,只有 plane 的 Layer ,用于下面射线判定
LayerMask mask2 = 1 << (LayerMask.NameToLayer("plane"));
//第二个射线,如果射线在2M范围内碰到 mask2 的物体,进入此分支
if (Physics.Raycast(ray, out hitInfoPlane,2, mask2.value))
{
Debug.Log("Plane " + hitInfoPlane.collider.gameObject.name + " " + hitInfoPlane.collider.gameObject.layer);
//移动物体
go.transform.position = hitInfoPlane.point;
}
}
}
}
}
}
测试与不足
现在实现效果:
刚识别时:
拖拽后:
拖拽效果还不错,但其他还有问题:
- 实验操作板需要设置成透明的。
- 目前 plane 只有一侧能够碰撞,也就是只能从一侧操作它。
- 需要限制操作空间,不能把化学仪器拖动太远。
其他:Unity 发布 Android 项目时 Debug
不能用Unity自带的 Console 窗口,参考博客 :https://blog.csdn.net/qq_15020543/article/details/85028308
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。