http://www.jpct.net/wiki/index.php/MultiTouch_camera_controls

MultiTouch camera controls

This code will handle control from 1 or 2 fingers, it was made for a tablet showing 3D objects, so it's made for navigating not for FPS gaming.

Usage Call MovementHandler.handleMotionEvent for every motionEvent you receive. CAll MovementHandler.getMovement for every tick or whenever you can to update the movement.

import android.util.Log;
import android.view.MotionEvent; /**
*
* @author Ruben.Hesselbaek@beumergroup.com
*
*/
public class MovementHandler { /**
* Logs a lot of stuff
*/
public static final boolean DEBUG_MOVEMENT = false; /**
* Min 0.25 degrees
*/
public static final float MIN_ANGLE_XMOVEMENT = (float) (Math.PI / 720); /**
* Max 15 degrees
*/
public static final float MAX_ANGLE_XMOVEMENT = (float) MIN_ANGLE_XMOVEMENT * 60; /**
* Pixels->Angle ratio on the X-axis
*/
public static final float XANGLE_FACTOR = 360; /**
* Min 0.25 degrees
*/
public static final float MIN_ANGLE_YMOVEMENT = (float) (Math.PI / 720); /**
* Max 15 degrees
*/
public static final float MAX_ANGLE_YMOVEMENT = (float) MIN_ANGLE_YMOVEMENT * 60; /**
* Pixels->Angle ratio on the Y-axis
*/
public static final float YANGLE_FACTOR = 360; /**
* Pixels->Distance ratio
*/
public static final float DISTANCE_FACTOR = 10; /**
* Pixels->Movement ratio
*/
public static final float MOVEMENT_X_FACTOR = 10; /**
* Pixels->Movement ratio
*/
public static final float MOVEMENT_Y_FACTOR = 10; public static final float MIN_MOVEMENT_DELTA = 0.1f;
public static final float MAX_MOVEMENT_DELTA = 5; private int _firstPointerId = MotionEvent.INVALID_POINTER_ID;
private float startMovementx1;
private float startMovementy1;
private float lastMovementx1;
private float lastMovementy1; private int _secondPointerId = MotionEvent.INVALID_POINTER_ID;
private float startMovementx2;
private float startMovementy2;
private float lastMovementy2;
private float lastMovementx2; private float startDistance;
private float lastDistance; private float startAngle;
private float lastAngle; public synchronized Movement getMovement() {
Movement movement = new Movement(); if (_secondPointerId != MotionEvent.INVALID_POINTER_ID) {
float movementX1 = lastMovementx1 - startMovementx1;
float movementY1 = lastMovementy1 - startMovementy1;
float movementX2 = lastMovementx2 - startMovementx2;
float movementY2 = lastMovementy2 - startMovementy2; if ((movementX1 > 0 && movementX2 > 0)
|| (movementX1 < 0 && movementX2 < 0)) {
float movementX = Math.min(Math.abs(movementX1),
Math.abs(movementX2))
/ MOVEMENT_X_FACTOR;
if (movementX > MIN_MOVEMENT_DELTA
&& movementX < MAX_MOVEMENT_DELTA) {
if (movementX1 > 0) {
movement.cameraMovementX = movementX;
} else {
movement.cameraMovementX = -movementX;
}
} else {
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler",
"getMovement(): Invalid movement: movementX=<"
+ movementX + ">, movementX1=<"
+ movementX1 + ">, movementX2=<"
+ movementX2 + ">");
}
}
} if ((movementY1 > 0 && movementY2 > 0)
|| (movementY1 < 0 && movementY2 < 0)) {
float movementY = Math.min(Math.abs(movementY1),
Math.abs(movementY2))
/ MOVEMENT_Y_FACTOR;
if (movementY > MIN_MOVEMENT_DELTA
&& movementY < MAX_MOVEMENT_DELTA) {
if (movementY1 > 0) {
movement.cameraMovementY = movementY;
} else {
movement.cameraMovementY = -movementY;
}
} else {
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler",
"getMovement(): Invalid movement: movementY<"
+ movementY + ">, movementY1=<"
+ movementY1 + ">, movementY2=<"
+ movementY2 + ">");
}
}
}
float distanceDelta = lastDistance - startDistance;
float distance = distanceDelta / DISTANCE_FACTOR;
float absoluteDistance = Math.abs(distance);
if (absoluteDistance > MIN_MOVEMENT_DELTA
&& absoluteDistance < MAX_MOVEMENT_DELTA) {
movement.cameraMovementZ = distance;
} else {
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler",
"getMovement(): Invalid movement: distanceDelta=<"
+ distanceDelta + ">, distance=<"
+ distance + ">");
}
} float angleDelta = lastAngle - startAngle;
float absoluteAngle = Math.abs(angleDelta);
if (absoluteAngle > MIN_ANGLE_XMOVEMENT
&& absoluteAngle < MAX_ANGLE_XMOVEMENT) {
movement.cameraRotationY = angleDelta;
} else {
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler",
"getMovement(): Invalid movement: absoluteAngle=<"
+ absoluteAngle + ">, startAngle=<"
+ Math.toDegrees(startAngle)
+ ">, lastAngle=<"
+ Math.toDegrees(lastAngle) + ">");
}
}
} else if (_firstPointerId != MotionEvent.INVALID_POINTER_ID) {
float movementX1 = lastMovementx1 - startMovementx1;
float angle = (float) ((movementX1) / XANGLE_FACTOR);
float absoluteAngle = Math.abs(angle);
if (absoluteAngle > MIN_ANGLE_XMOVEMENT
&& absoluteAngle < MAX_ANGLE_XMOVEMENT) {
movement.cameraRotationY = angle;
} else {
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler",
"getMovement(): Invalid movement: movementX=<"
+ movementX1 + ">, angle=<" + absoluteAngle
+ ">");
}
} float movementY1 = lastMovementy1 - startMovementy1;
angle = (float) ((movementY1) / YANGLE_FACTOR);
absoluteAngle = Math.abs(angle);
if (absoluteAngle > MIN_ANGLE_YMOVEMENT
&& absoluteAngle < MAX_ANGLE_YMOVEMENT) {
movement.worldRotationX = angle;
} else {
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler",
"getMovement(): Invalid movement: movementY=<"
+ movementY1 + ">, angle=<" + absoluteAngle
+ ">");
}
}
}
startMovementx1 = lastMovementx1;
startMovementy1 = lastMovementy1;
startMovementx2 = lastMovementx2;
startMovementy2 = lastMovementy2;
startDistance = lastDistance;
startAngle = lastAngle;
return movement;
} public synchronized void handleMotionEvent(MotionEvent event) {
final int pointerIndex = event.getActionIndex();
final int pointerId = event.getPointerId(pointerIndex); if (DEBUG_MOVEMENT) {
Log.d("MovementHandler", "handleMotionEvent(pointerIndex=<"
+ pointerIndex + ">, pointerId=<" + pointerId
+ ">, event=<" + event.getActionMasked()
+ ">, pointerCount=<" + event.getPointerCount() + ">");
}
switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN: {
_firstPointerId = pointerId;
_secondPointerId = MotionEvent.INVALID_POINTER_ID;
startMovementx1 = event.getX(_firstPointerId);
startMovementy1 = event.getY(_firstPointerId);
lastMovementx1 = startMovementx1;
lastMovementy1 = startMovementy1;
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler", "ACTION_DOWN(firstPointerId=<"
+ _firstPointerId + ">(" + startMovementx1 + ","
+ startMovementy1 + ")");
}
break;
}
case MotionEvent.ACTION_UP: {
_firstPointerId = MotionEvent.INVALID_POINTER_ID;
_secondPointerId = MotionEvent.INVALID_POINTER_ID;
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler", "ACTION_UP(firstPointerId=<"
+ _firstPointerId + ">)");
}
break;
}
case MotionEvent.ACTION_POINTER_DOWN:
if (event.getPointerCount() == 2) {
_secondPointerId = pointerId;
startMovementx2 = event.getX(_secondPointerId);
startMovementy2 = event.getY(_secondPointerId);
lastMovementx2 = startMovementx2;
lastMovementy2 = startMovementy2;
startDistance = calcDistance();
startAngle = calcAngle();
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler",
"ACTION_POINTER_DOWN(secondPointerId=<"
+ _secondPointerId + ">)");
}
}
break;
case MotionEvent.ACTION_POINTER_UP:
if (pointerId == _firstPointerId) {
// This was our active pointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
_firstPointerId = event.getPointerId(newPointerIndex);
int oldPointerId = _firstPointerId;
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler",
"ACTION_POINTER_UP(_firstPointerId=<"
+ _firstPointerId + ">, oldPointerId=<"
+ oldPointerId + ">)");
}
startMovementx1 = event.getX(newPointerIndex);
startMovementy1 = event.getY(newPointerIndex);
} else if (pointerId == _secondPointerId) {
if (event.getPointerCount() > 2) {
// This was our active secondpointer going up. Choose a new
// active pointer and adjust accordingly.
final int newPointerIndex = pointerIndex == 1 ? 2 : 1;
_secondPointerId = event.getPointerId(newPointerIndex);
int oldPointerId = _secondPointerId;
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler",
"ACTION_POINTER_UP(secondPointerId=<"
+ _secondPointerId
+ ">, oldPointerId=<" + oldPointerId
+ ">)");
}
startMovementx2 = event.getX(newPointerIndex);
startMovementy2 = event.getY(newPointerIndex);
} else {
_secondPointerId = MotionEvent.INVALID_POINTER_ID;
if (DEBUG_MOVEMENT) {
Log.d("MovementHandler",
"ACTION_POINTER_UP(secondPointerId=<"
+ _secondPointerId + ">)");
}
}
}
break;
case MotionEvent.ACTION_MOVE: {
int firstPointerIndex = event.findPointerIndex(_firstPointerId);
int secondPointerIndex = event.findPointerIndex(_secondPointerId);
if (event.getPointerCount() > 0) {
lastMovementx1 = event.getX(firstPointerIndex);
lastMovementy1 = event.getY(firstPointerIndex);
}
if (event.getPointerCount() > 1) {
lastMovementx2 = event.getX(secondPointerIndex);
lastMovementy2 = event.getY(secondPointerIndex);
lastDistance = calcDistance();
lastAngle = calcAngle();
}
break;
}
}
} private float calcDistance() {
float deltaX = lastMovementx2 - lastMovementx1;
float deltaY = lastMovementy2 - lastMovementy1;
return (float) Math.sqrt(deltaX * deltaX + deltaY * deltaY);
} private float calcAngle() {
return (float) Math.atan2(lastMovementx1 - lastMovementx2,
lastMovementy1 - lastMovementy2);
}
}
/**
*
* @author Ruben.Hesselbaek@beumergroup.com
*
*/
public class Movement { public float worldRotationX;
public float cameraRotationY;
public float cameraMovementX;
public float cameraMovementY;
public float cameraMovementZ; public boolean hasMovement() {
return worldRotationX != 0f || cameraRotationY != 0f
|| cameraMovementX != 0f || cameraMovementY != 0f
|| cameraMovementZ != 0f;
} @Override
public String toString() {
return "Movement (hasMovement=<"+hasMovement()+">, worldRotationX=" + worldRotationX
+ ", cameraRotationY=" + cameraRotationY + ", cameraMovementX="
+ cameraMovementX + ", cameraMovementY=" + cameraMovementY
+ ", cameraMovementZ=" + cameraMovementZ + ")";
} }
@Override
public boolean onTouchEvent(MotionEvent event) { _movementHandler.handleMotionEvent(event);
return true;
} private void moveCamera() {
Movement movement = _movementHandler.getMovement(); if (movement.hasMovement()) {
Camera camera = world.getCamera(); Matrix rot = camera.getBack(); rot.rotateAxis(rot.getYAxis(), -(float) movement.cameraRotationY);
worldObject.rotateX(movement.worldRotationX);
float angle = worldObject.getYAxis().calcAngle(
new SimpleVector(0, 1, 0));
float crossAngle = worldObject.getYAxis().calcCross(
new SimpleVector(0, 1, 0)).x;
if (crossAngle > 0) {
worldObject.rotateX(-angle);
} else {
float delta = (float) (angle - (Math.PI / 2));
if (delta > 0) {
worldObject.rotateX(delta);
}
} camera.moveCamera(Camera.CAMERA_MOVELEFT, movement.cameraMovementX);
camera.moveCamera(Camera.CAMERA_MOVEUP, movement.cameraMovementY);
camera.moveCamera(Camera.CAMERA_MOVEIN, movement.cameraMovementZ);
}
}

MultiTouch camera controls source code的更多相关文章

  1. Website's Game source code

    A Darkroom by doublespeakgames <!DOCTYPE html> <html itemscope itemtype="https://schem ...

  2. Tips for newbie to read source code

    This post is first posted on my WeChat public account: GeekArtT Reading source code is always one bi ...

  3. 编程等宽字体Source Code Pro(转)

    Source Code Pro - 最佳的免费编程字体之一!来自 Adobe 公司的开源等宽字体下载     每一位程序员都有一套自己喜爱的代码编辑器与编程字体,譬如我们之前就推荐过一款"神 ...

  4. How to build the Robotics Library from source code on Windows

    The Robotics Library is an open source C++ library for robot kinematics, motion planning and control ...

  5. How to build windows azure PowerShell Source Code

    Download any version source code of Windows Azure Powershell from https://github.com/Azure/azure-sdk ...

  6. akka cluster sharding source code 学习 (1/5) 替身模式

    为了使一个项目支持集群,自己学习使用了 akka cluster 并在项目中实施了,从此,生活就变得有些痛苦.再配上 apache 做反向代理和负载均衡,debug 起来不要太酸爽.直到现在,我还对 ...

  7. view class source code with JAD plugin in Eclipse

    The default class viewer doesn't decompile the class file so you cannot open and check the source co ...

  8. Classic Source Code Collected

    收藏一些经典的源码,持续更新!!! 1.深度学习框架(Deep Learning Framework). A:Caffe (Convolutional Architecture for Fast Fe ...

  9. Attach source code to a Netbeans Library Wrapper Module

    http://rubenlaguna.com/wp/2008/02/22/attach-source-code-to-a-netbeans-library-wrapper-module/ Attach ...

随机推荐

  1. Hive优化(转)

    一.join优化 Join查找操作的基本原则:应该将条目少的表/子查询放在 Join 操作符的左边.原因是在 Join 操作的 Reduce 阶段,位于 Join 操作符左边的表的内容会被加载进内存, ...

  2. UILocalNotification本地通知

    // 执行通知一定要退出应用或挂起应用(进入后台)才能收到通知. 1.在iOS8及其以后版本中使用本地消息需要先获得用户的许可,否则无法成功注册本地消息.因此,我们将询问用户许可的代码片段添加到了ap ...

  3. 什么是UI控件

    凡是带有Widget参数的组件都是控件.

  4. CoreText实现图文混排之点击事件-b

    CoreText实现图文混排之点击事件 主要思路 我们知道,CoreText是基于UIView去绘制的,那么既然有UIView,就有 -(void)touchesBegan:(NSSet<UIT ...

  5. Automotive Security的一些资料和心得(5):Privacy

    1. Introduction 1.1 "Customers own their data and we can be no more than the trsted stewards of ...

  6. pgrep 查询进程的工具

    pgrep 1:简介 pgrep 是通过程序的名字来查询进程的工具,一般是用来判断程序是否正在运行.在服务器的配置和管理中,这个工具常被应用,简单明了: 1:用法 #pgrep 参数选项 程序名 常用 ...

  7. 纯CSS3代码实现表格奇偶行异色,鼠标悬浮变色

    1.首先会用到<tr></tr>元素两个伪类,nth-child()和hover. 然后需要注意的是伪类都是通过冒号引用的,不是点号,即tr:hover{} 其次,CSS代码中 ...

  8. PHP file_get_contents() 函数

    定义和用法 file_get_contents() 函数把整个文件读入一个字符串中. 和 file() 一样,不同的是 file_get_contents() 把文件读入一个字符串. file_get ...

  9. Android 设置thumb图片大小

    xml: android:thumb="@drawable/seekbar_thumb" seekbar_thumb.xml: <?xml version="1.0 ...

  10. Android开发规范

    一.Android编码规范 1.java代码中不出现中文,最多注释中可以出现中文 2.局部变量命名.静态成员变量命名 只能包含字母,单词首字母出第一个外,都为大写,其他字母都为小写 3.常量命名 只能 ...