在 CardBoard 和 Rajawali VR 中使用触摸事件移动相机 Android
Move camera around using touch event in CardBoard and Rajawali VR Android
目前,我正在为 android 开发使用 google CardBoard 和 Rajawali 播放 360 度视频的 VR 应用程序。传感器运行良好,但我无法使用触摸正确拖动场景或相机。有没有办法在这个应用程序中启用触摸模式?
非常感谢任何帮助!
谢谢。
我也做过同样的事情,这是我用的:
首先,看一下 Rajawali 的 ArcballCamera class。您可以在那里看到如何处理触摸事件以通过触摸事件旋转相机。
问题是当用户在屏幕上移动时我不喜欢旋转的默认行为,所以我自己做了另一个实现,基于前一个实现并直接旋转我想要的球体而不是相机,所以就这样了(所有这些都在我的渲染器中class,顺便说一句):
首先声明:
private GestureDetector detector; //gesture detector
private ScaleGestureDetector scaleDetector; //scale detector (for zooming)
private GestureListener gListener; //gesture listener
private ScaleListener sListener; //scale listener
private View.OnTouchListener touchListener; //touch events listener
private boolean isRotating; //true if the sphere is rotating
private boolean isScaling; //true if the sphere is scaling
private float xInicial,yInicial; //inicial touch point
//sphere's yaw and pitch, used for rotation
private double yaw,pitch, yawAcumulado=0, pitchAcumulado=0, yawAcumuladoR=0, pitchAcumuladoR=0;
//physical to logical (in 3D world) conversion: screen scroll to sphere rotation
private final double gradosPorBarridoX=120, gradosPorBarridoY=90;
private final double gradosPorPixelYaw, gradosPorPixelPitch;
在渲染器的构造函数中我开始初始化(定时器和控件用于视频控制视图,所以不要关注这些):
DisplayMetrics outMetrics = new DisplayMetrics();
((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
gradosPorPixelPitch = gradosPorBarridoY / outMetrics.heightPixels;
gradosPorPixelYaw = gradosPorBarridoX / outMetrics.widthPixels;
addListeners();
...
//from Rajawali ArcballCamera class
private void addListeners(){
((Activity)context).runOnUiThread(new Runnable() {
@Override
public void run() {
gListener = new GestureListener();
sListener = new ScaleListener();
detector = new GestureDetector(context, gListener);
scaleDetector = new ScaleGestureDetector(context, sListener);
touchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
scaleDetector.onTouchEvent(event); //see if it is a scale event
//if not, check whether it is a scroll
if (!isScaling) {
detector.onTouchEvent(event);
//or an up motion
if (event.getAction() == MotionEvent.ACTION_UP) {
if (!isRotating) {
//change video control view's visibility
TouchActivity.timer.cancel();
if (TouchActivity.control.getVisibility() == View.INVISIBLE) {
TouchActivity.control.setVisibility(View.VISIBLE);
TouchActivity.timer.start(); //timer is restarted
} else {
TouchActivity.control.setVisibility(View.INVISIBLE);
}
} else {
isRotating = false; //cancel rotation
}
}
}
return true;
}
};
TouchActivity.principal.setOnTouchListener(touchListener);
}
});
}
最后但同样重要的是,监听事件(缩放和旋转):
/**
* called when the rotation starts
* @param x
* @param y
*/
private void startRotation(float x, float y){
xInicial = x;
yInicial = y;
}
/**
* called during the consecutive events of a rotation movement
* @param x
* @param y
*/
private void updateRotation(float x, float y){
float difX = xInicial - x;
float difY = yInicial - y;
yaw= difX * gradosPorPixelYaw;
pitch = difY * gradosPorPixelPitch;
yawAcumulado+=yaw;
pitchAcumulado+=pitch;
}
/**
* event listener. if the user scrolls his finger through the screen, it sends the
* touch event to calculate the sphere's rotation
*/
private class GestureListener extends GestureDetector.SimpleOnGestureListener{
@Override
public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX, float distanceY) {
//starts or updates the rotation with the upcoming event x and y screen values
if(!isRotating) {
startRotation(event2.getX(), event2.getY());
isRotating=true;
return false;
}else{
isRotating = true;
updateRotation(event2.getX(), event2.getY());
return false;
}
}
}
/**
* event listener. Zooms in or out depending on the user's action
*/
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener{
//zooms in or out according to the scale detector value
@Override
public boolean onScale(ScaleGestureDetector detector) {
if(detector.getScaleFactor()>1){
if(earthSphere.getScaleX()*1.1<120){
earthSphere.setScaleX(earthSphere.getScaleX()*1.1);
earthSphere.setScaleY(earthSphere.getScaleY() * 1.1);
earthSphere.setScaleZ(earthSphere.getScaleZ() * 1.1);
}
}else{
if(earthSphere.getScaleX()*0.9>0.95) {
earthSphere.setScaleX(earthSphere.getScaleX() * 0.9);
earthSphere.setScaleY(earthSphere.getScaleY() * 0.9);
earthSphere.setScaleZ(earthSphere.getScaleZ() * 0.9);
}
}
return true;
}
//the zoom begins
@Override
public boolean onScaleBegin (ScaleGestureDetector detector) {
isScaling = true;
isRotating = false;
return super.onScaleBegin(detector);
}
//the zoom ends
@Override
public void onScaleEnd (ScaleGestureDetector detector) {
isRotating = false;
isScaling = false;
}
}
所有这些都解决后,您只需在每个渲染上设置方向,如下所示:
yawAcumuladoR = (yawAcumulado) * 0.04;
pitchAcumuladoR = (pitchAcumulado) * 0.04;
Quaternion q = new Quaternion();
q.fromEuler(yawAcumuladoR, pitchAcumuladoR, 0);
earthSphere.setOrientation(q);
正如我所说,这对我有用,但我只是在旋转球体。将它添加到您的需要应该不难,此外,您拥有作为相机的 Arcball class,可能更适合您的需求。总之,希望对你有用。
目前,我正在为 android 开发使用 google CardBoard 和 Rajawali 播放 360 度视频的 VR 应用程序。传感器运行良好,但我无法使用触摸正确拖动场景或相机。有没有办法在这个应用程序中启用触摸模式?
非常感谢任何帮助! 谢谢。
我也做过同样的事情,这是我用的:
首先,看一下 Rajawali 的 ArcballCamera class。您可以在那里看到如何处理触摸事件以通过触摸事件旋转相机。
问题是当用户在屏幕上移动时我不喜欢旋转的默认行为,所以我自己做了另一个实现,基于前一个实现并直接旋转我想要的球体而不是相机,所以就这样了(所有这些都在我的渲染器中class,顺便说一句):
首先声明:
private GestureDetector detector; //gesture detector
private ScaleGestureDetector scaleDetector; //scale detector (for zooming)
private GestureListener gListener; //gesture listener
private ScaleListener sListener; //scale listener
private View.OnTouchListener touchListener; //touch events listener
private boolean isRotating; //true if the sphere is rotating
private boolean isScaling; //true if the sphere is scaling
private float xInicial,yInicial; //inicial touch point
//sphere's yaw and pitch, used for rotation
private double yaw,pitch, yawAcumulado=0, pitchAcumulado=0, yawAcumuladoR=0, pitchAcumuladoR=0;
//physical to logical (in 3D world) conversion: screen scroll to sphere rotation
private final double gradosPorBarridoX=120, gradosPorBarridoY=90;
private final double gradosPorPixelYaw, gradosPorPixelPitch;
在渲染器的构造函数中我开始初始化(定时器和控件用于视频控制视图,所以不要关注这些):
DisplayMetrics outMetrics = new DisplayMetrics();
((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
gradosPorPixelPitch = gradosPorBarridoY / outMetrics.heightPixels;
gradosPorPixelYaw = gradosPorBarridoX / outMetrics.widthPixels;
addListeners();
...
//from Rajawali ArcballCamera class
private void addListeners(){
((Activity)context).runOnUiThread(new Runnable() {
@Override
public void run() {
gListener = new GestureListener();
sListener = new ScaleListener();
detector = new GestureDetector(context, gListener);
scaleDetector = new ScaleGestureDetector(context, sListener);
touchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
scaleDetector.onTouchEvent(event); //see if it is a scale event
//if not, check whether it is a scroll
if (!isScaling) {
detector.onTouchEvent(event);
//or an up motion
if (event.getAction() == MotionEvent.ACTION_UP) {
if (!isRotating) {
//change video control view's visibility
TouchActivity.timer.cancel();
if (TouchActivity.control.getVisibility() == View.INVISIBLE) {
TouchActivity.control.setVisibility(View.VISIBLE);
TouchActivity.timer.start(); //timer is restarted
} else {
TouchActivity.control.setVisibility(View.INVISIBLE);
}
} else {
isRotating = false; //cancel rotation
}
}
}
return true;
}
};
TouchActivity.principal.setOnTouchListener(touchListener);
}
});
}
最后但同样重要的是,监听事件(缩放和旋转):
/**
* called when the rotation starts
* @param x
* @param y
*/
private void startRotation(float x, float y){
xInicial = x;
yInicial = y;
}
/**
* called during the consecutive events of a rotation movement
* @param x
* @param y
*/
private void updateRotation(float x, float y){
float difX = xInicial - x;
float difY = yInicial - y;
yaw= difX * gradosPorPixelYaw;
pitch = difY * gradosPorPixelPitch;
yawAcumulado+=yaw;
pitchAcumulado+=pitch;
}
/**
* event listener. if the user scrolls his finger through the screen, it sends the
* touch event to calculate the sphere's rotation
*/
private class GestureListener extends GestureDetector.SimpleOnGestureListener{
@Override
public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX, float distanceY) {
//starts or updates the rotation with the upcoming event x and y screen values
if(!isRotating) {
startRotation(event2.getX(), event2.getY());
isRotating=true;
return false;
}else{
isRotating = true;
updateRotation(event2.getX(), event2.getY());
return false;
}
}
}
/**
* event listener. Zooms in or out depending on the user's action
*/
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener{
//zooms in or out according to the scale detector value
@Override
public boolean onScale(ScaleGestureDetector detector) {
if(detector.getScaleFactor()>1){
if(earthSphere.getScaleX()*1.1<120){
earthSphere.setScaleX(earthSphere.getScaleX()*1.1);
earthSphere.setScaleY(earthSphere.getScaleY() * 1.1);
earthSphere.setScaleZ(earthSphere.getScaleZ() * 1.1);
}
}else{
if(earthSphere.getScaleX()*0.9>0.95) {
earthSphere.setScaleX(earthSphere.getScaleX() * 0.9);
earthSphere.setScaleY(earthSphere.getScaleY() * 0.9);
earthSphere.setScaleZ(earthSphere.getScaleZ() * 0.9);
}
}
return true;
}
//the zoom begins
@Override
public boolean onScaleBegin (ScaleGestureDetector detector) {
isScaling = true;
isRotating = false;
return super.onScaleBegin(detector);
}
//the zoom ends
@Override
public void onScaleEnd (ScaleGestureDetector detector) {
isRotating = false;
isScaling = false;
}
}
所有这些都解决后,您只需在每个渲染上设置方向,如下所示:
yawAcumuladoR = (yawAcumulado) * 0.04;
pitchAcumuladoR = (pitchAcumulado) * 0.04;
Quaternion q = new Quaternion();
q.fromEuler(yawAcumuladoR, pitchAcumuladoR, 0);
earthSphere.setOrientation(q);
正如我所说,这对我有用,但我只是在旋转球体。将它添加到您的需要应该不难,此外,您拥有作为相机的 Arcball class,可能更适合您的需求。总之,希望对你有用。