Por: Daniel Robledo
Con esta demo se quiere demostrar como el PS Move, a través de la consola Playstation 3, nos permite capturar sus datos de forma clara y como podemos utilizar los mismos para implementar aplicaciones en las cuales nos podamos mover en 3 dimensiones y recibir respuesta haptica entre otras, en este caso con la ayuda del motor de videojuegos Unity.
Consola PS3 con la aplicación Move.Me (descargada de PSN)
Tener un PS Move Motion Controller y un Playstation Eye
Tener un PC con la ultima version de Unity o mínimo la 3.5 y con conexión a internet: http://unity3d.com/unity/download/
El Move.me es un servidor de aplicaciones que utiliza el control PS Move con un input device. En otras palabras, permite al PS3 actuar como intermediario entre el PS Move y el PC.
Como se dijo anteriormente, el principal objetivo de este proyecto era encontrar una forma adecuada de conectar el Move.me con el motor de juegos Unity3D. Esto se logro mediante la implementación de un wrapper que nos permite la comunicación entre el motor y la aplicación cliente que por defecto podemos encontrar con el Move.me.
Básicamente este nos permite obtener los datos (como bytes) del cliente, transformarlos para su correcta interpretación y utilizarlos:
1. Cuando se esta conectado, el cliente PSMoveClient obtiene los datos
public void FillFromNetworkBuffer(ref Byte[] buffer, int i) { int offset = 104 + i * 176; pos = NetworkReaderHelper.ReadFloat4(ref buffer, offset); vel = NetworkReaderHelper.ReadFloat4(ref buffer, offset + 16); accel = NetworkReaderHelper.ReadFloat4(ref buffer, offset + 32); quat = NetworkReaderHelper.ReadFloat4(ref buffer, offset + 48); angvel = NetworkReaderHelper.ReadFloat4(ref buffer, offset + 64); angaccel = NetworkReaderHelper.ReadFloat4(ref buffer, offset + 80); handle_pos = NetworkReaderHelper.ReadFloat4(ref buffer, offset + 96); handle_vel = NetworkReaderHelper.ReadFloat4(ref buffer, offset + 112); handle_accel = NetworkReaderHelper.ReadFloat4(ref buffer, offset + 128); pad.FillFromNetworkBuffer(ref buffer, i); timestamp = NetworkReaderHelper.ReadInt64(ref buffer, offset + 152); temperature = NetworkReaderHelper.ReadFloat(ref buffer, offset + 160); camera_pitch_angle = NetworkReaderHelper.ReadFloat(ref buffer, offset + 164); tracking_flags = NetworkReaderHelper.ReadUint32(ref buffer, offset + 168); }
2. Después la clase PSMoveWrapper nos ayuda con las transformaciones correspondientes a posición, rotación y orientación entre otras.
private void UpdateGemState(int num) { bool selected_move_connected = (state.gemStatus[num].connected == 1); PSMoveSharpGemState selected_gem = state.gemStates[num]; position[num].x = (float)Convert.ToInt32(selected_gem.pos.x)/100; position[num].y = (float)Convert.ToInt32(selected_gem.pos.y)/100; position[num].z = (float)Convert.ToInt32(selected_gem.pos.z)/100; velocity[num].x = (float)Convert.ToInt32(selected_gem.vel.x)/100; velocity[num].y = (float)Convert.ToInt32(selected_gem.vel.y)/100; velocity[num].z = (float)Convert.ToInt32(selected_gem.vel.z)/100; acceleration[num].x = (float)Convert.ToInt32(selected_gem.accel.x)/100; acceleration[num].y = (float)Convert.ToInt32(selected_gem.accel.y)/100; acceleration[num].z = (float)Convert.ToInt32(selected_gem.accel.z)/100; Quaternion rotation = new Quaternion(selected_gem.quat.x, selected_gem.quat.y, selected_gem.quat.z, selected_gem.quat.w); orientation[num] = rotation.eulerAngles; qOrientation[num] = rotation; angularVelocity[num].x = Convert.ToInt32((180.0 / Math.PI) * selected_gem.angvel.x); angularVelocity[num].y = Convert.ToInt32((180.0 / Math.PI) * selected_gem.angvel.y); angularVelocity[num].z = Convert.ToInt32((180.0 / Math.PI) * selected_gem.angvel.z); angularAcceleration[num].x = Convert.ToInt32((180.0 / Math.PI) * selected_gem.angaccel.x); angularAcceleration[num].y = Convert.ToInt32((180.0 / Math.PI) * selected_gem.angaccel.y); angularAcceleration[num].z = Convert.ToInt32((180.0 / Math.PI) * selected_gem.angaccel.z); handlePosition[num].x = (float)Convert.ToInt32(selected_gem.handle_pos.x)/100; handlePosition[num].y = (float)Convert.ToInt32(selected_gem.handle_pos.y)/100; handlePosition[num].z = (float)Convert.ToInt32(selected_gem.handle_pos.z)/100; handleVelocity[num].x = (float)Convert.ToInt32(selected_gem.handle_vel.x)/100; handleVelocity[num].y = (float)Convert.ToInt32(selected_gem.handle_vel.y)/100; handleVelocity[num].z = (float)Convert.ToInt32(selected_gem.handle_vel.z)/100; handleAcceleration[num].x = (float)Convert.ToInt32(selected_gem.handle_accel.x)/100; handleAcceleration[num].y = (float)Convert.ToInt32(selected_gem.handle_accel.y)/100; handleAcceleration[num].z = (float)Convert.ToInt32(selected_gem.handle_accel.z)/100; }
3. Luego podemos utlizar los datos para por ejemplo, mover un objeto basandonos en los datos del PS Move.
void FixedUpdate () { Vector3 gemPos, handlePos; gemPos = psMoveWrapper.position[0]; handlePos = psMoveWrapper.handlePosition[0]; if(isMirror) { gem.transform.localPosition = gemPos; handle.transform.localPosition = handlePos; handle.transform.localRotation = Quaternion.Euler(psMoveWrapper.orientation[0]); } else { gemPos.z = -gemPos.z + zOffset; handlePos.z = -handlePos.z + zOffset; gem.transform.localPosition = gemPos; handle.transform.localPosition = handlePos; handle.transform.localRotation = Quaternion.LookRotation(gemPos - handlePos); } }
*Este wrapper esta basado en la solución dada por Xun Zhang