OCULUS QEUST2를 사용하여 Right Handler의 A버튼을 눌렀을 때, 조준점에 복셀(Voxel : 3차원 큐브)을 무한대로 생성하는 미니 게임을 제작해보도록 한다.
VR 복셀 생성 게임 만드는 방법
1) 기본 배경 및 복셀 세팅
먼저 새로운 씬에 해당 파일을 임포트해준다. 다음 패키지는 oculus quest2 실행과 handler 작동에 관한 tool kit으로 생각하면 되겠다. 그리고 하이라키 상에 Cube 생성 후 Voxel로 rename, Create Empty 생성 후 Voxel Maker로 rename 해준다(각각 스크립트와 연결)
2) 조준점 UI 세팅
하이라키 창에서 UI - Image를 생성, Canvas 이름을 Crosshair로 rename 후 Transform 값 적절히 세팅, Render Mode를 World Space로 변경 / Image에 조준점 이미지 파일 2D Sprite 형식으로 변경 후 apply
3) 내장 쉐이더 설치
유니티3D 다운로드로 가서 자신의 버전에 맞는 유니티 버전을 찾는다. 그리고 다음과 같이 자신이 사용하고 있는 운영체제에 맞는 버전을 찾고 "내장 셰이더"를 다운 받아 준다. 설치 후 BuildInResouceExtras - UI - UI Default.shader 파일을조준점 이미지(Crosshair)가 속한 폴더에 복붙해준다. 해당 파일을 Crosshair로 rename 해준다.
// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
Shader "Crosshair"
{
Properties
{
[PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1,1,1,1)
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_ColorMask ("Color Mask", Float) = 15
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
Cull Off
Lighting Off
ZWrite Off
ZTest Always
Blend One OneMinusSrcAlpha
ColorMask [_ColorMask]
Pass
{
Name "Default"
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 2.0
#include "UnityCG.cginc"
#include "UnityUI.cginc"
#pragma multi_compile_local _ UNITY_UI_CLIP_RECT
#pragma multi_compile_local _ UNITY_UI_ALPHACLIP
struct appdata_t
{
float4 vertex : POSITION;
float4 color : COLOR;
float2 texcoord : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 vertex : SV_POSITION;
fixed4 color : COLOR;
float2 texcoord : TEXCOORD0;
float4 worldPosition : TEXCOORD1;
half4 mask : TEXCOORD2;
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _MainTex;
fixed4 _Color;
fixed4 _TextureSampleAdd;
float4 _ClipRect;
float4 _MainTex_ST;
float _UIMaskSoftnessX;
float _UIMaskSoftnessY;
v2f vert(appdata_t v)
{
v2f OUT;
UNITY_SETUP_INSTANCE_ID(v);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(OUT);
float4 vPosition = UnityObjectToClipPos(v.vertex);
OUT.worldPosition = v.vertex;
OUT.vertex = vPosition;
float2 pixelSize = vPosition.w;
pixelSize /= float2(1, 1) * abs(mul((float2x2)UNITY_MATRIX_P, _ScreenParams.xy));
float4 clampedRect = clamp(_ClipRect, -2e10, 2e10);
float2 maskUV = (v.vertex.xy - clampedRect.xy) / (clampedRect.zw - clampedRect.xy);
OUT.texcoord = TRANSFORM_TEX(v.texcoord.xy, _MainTex);
OUT.mask = half4(v.vertex.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_UIMaskSoftnessX, _UIMaskSoftnessY) + abs(pixelSize.xy)));
OUT.color = v.color * _Color;
return OUT;
}
fixed4 frag(v2f IN) : SV_Target
{
half4 color = IN.color * (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd);
#ifdef UNITY_UI_CLIP_RECT
half2 m = saturate((_ClipRect.zw - _ClipRect.xy - abs(IN.mask.xy)) * IN.mask.zw);
color.a *= m.x * m.y;
#endif
#ifdef UNITY_UI_ALPHACLIP
clip (color.a - 0.001);
#endif
color.rgb *= color.a;
return color;
}
ENDCG
}
}
}
crosshair 스크립트에서는 2가지 부분만 수정해주면 된다. 먼저 맨 상단에 있는 shader 이름을 "Crosshair"로 바꿔준다. 그리고 44번 째 줄을 "ZTEST Always" 로 수정해주면 된다. 스크립트 수정이 완성되면 해당 파일이 속한 하이라키 창에서 새로운 material을 생성 후 standard - Crosshair로 변경해주면 정상적으로 매핑된다. 마지막으로 Image의 Source Image와 material에 생성한 각각의 오브젝트를 연결해주면 된다.
4) 유니티 VR 세팅
생성된 복셀은 prefab화 시켜주고 기존의 복셀을 제거하며 복셀 메이커 창에 소스를 각각 매핑해준다. 그 다음은 OCULUS 개발 환경(Oculus xr plugin 설치 및 Oculus Integration 임포트)이 세팅되어 있다는 가정 하에, MainCamera 삭제 후 OVRCameraRig를 찾아 적절하게 세팅해 준다.
처음에 임포트했던 패키지 파일에 있는 스크립트 ARAVRInput에서 최상단의 3가지 중 다음과 같이 Oculus를 활성화 해주면 기본적인 VR 컨트롤 환경이 세팅된다. 구체적으로 A버튼을 눌렀을 때 복셀을 생성하기 위한 스크립트라고 이해하면 되겠다.
5) Voxel / VoxelMaker 스크립트
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Voxel : MonoBehaviour
{
public float speed = 5; //복셀이 날아갈 속도 구하기
public float destroyTime = 3.0f; //복셀을 제거할 시간
float currentTime; //경과 시간
void OnEnable() //오브젝트가 활성화 될 때 호출되는 콜백 함수
{
currentTime = 0;
Vector3 direction = Random.insideUnitSphere; //랜덤한 방향을 찾는다
Rigidbody rb = gameObject.GetComponent<Rigidbody>();
rb.velocity = direction * speed; //랜덤한 방향으로 날아가는 속도를 준다
}
void Update()
{
currentTime += Time.deltaTime; //시간을 누적
if(currentTime > destroyTime) //3초가 지났다면
{
gameObject.SetActive(false); //Voxel을 비활성화
VoxelMaker.voxelPool.Add(gameObject); //오브젝트 풀에 다시 넣어준다
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class VoxelMaker : MonoBehaviour
{
public GameObject voxelFactory; //복셀 공장
public int voxelPoolSize = 20; //오브젝트 풀의 크기
public static List<GameObject> voxelPool = new List<GameObject>(); //오브젝트 풀
public float createTime = 0.1f; //생성 시간
float currentTime = 0; //경과 시간
public Transform crosshair; // 크로스헤어(조준점) 변수
void Start()
{
for(int i = 0; i < voxelPoolSize; i++) //오브젝트 풀에 비활성화 된 복셀을 담는다
{
GameObject voxel = Instantiate(voxelFactory); //복셀 공장에서 복셀 생성
voxel.SetActive(false); //복셀 비활성화하기
voxelPool.Add(voxel); //복셀을 오브젝트 풀에 담는다
}
}
void Update()
{
ARAVRInput.DrawCrosshair(crosshair); //크로스헤어(조준점) 그리기
if (ARAVRInput.Get(ARAVRInput.Button.One)) //VR 컨트롤러의 발서 버튼을 누르면
{
currentTime += Time.deltaTime; //일정시간 마다 복셀을 만든다
if (currentTime > createTime)
{
//Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); //pc 일 때 마우스
//컨트롤러가 향하는 방향으로 시선 만들기
Ray ray = new Ray(ARAVRInput.RHandPosition, ARAVRInput.RHandDirection);
RaycastHit hitInfo = new RaycastHit();
if (Physics.Raycast(ray, out hitInfo)) //시선이 바닥 위에 위치해 있다면
{
if (voxelPool.Count > 0)
{
currentTime = 0; //복셀을 생성했을 때만 경과 시간을 초기화 해준다
GameObject voxel = voxelPool[0]; //오브젝트 풀에서 복셀 하나를 가져온다
voxel.SetActive(true); //복셀을 활성화한다
voxel.transform.position = hitInfo.point; //복셀을 배치한다
voxelPool.RemoveAt(0); //오브젝트 풀에서 복셀을 제거한다
}
}
}
}
}
}
Oculus를 Desktop과 연결 후 Link 모드로 실행해 보았다. Right Handler를 사용하여 조준점을 맞추고 A키를 눌렀더니 다음과 같이 복셀들이 계속 생성되었다가 일정 시간이 지나면 사라지는 것을 볼 수 있었다.
'게임 프로그래밍 > 유니티 프로젝트' 카테고리의 다른 글
[VR 개발]360 VR Capture를 통해 360 Panoramic 이미지 만들기 및 이미지 (0) | 2021.10.01 |
---|---|
[VR 개발]HDR Panoramic 360 VR 이미지 만들기 (0) | 2021.09.30 |
[VR 개발]유니티 VR환경 기본 세팅(OVRPlayerController, OVRCameraRig, LocalAvatar) (0) | 2021.09.03 |
[VR 개발]유니티를 활용하여 POLY & TILT BRUSH 툴킷 세팅 및 glb 파일 임포트하기 (2) | 2021.09.03 |
UnityChan을 활용하여 가위바위보 게임 만들기 (0) | 2021.08.25 |