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. 自定义MVC路由配置

    首先我用MVC4新增一个订单查看的功能 1.创建控制器OrderController namespace MvcApplication3.Controllers { public class Orde ...

  2. C++学习笔记37:元编程

    元编程 什么是元编程(metaprogramming) 利用模板可以进行编译期计算(数值计算,型式计算和代码计算)的特点进行程序设计 为什么可以进行元编程? C++是两层语言:执行编译期计算的代码称为 ...

  3. NetWorker常用术语

    术语 描述 active group 每24小时至少计划备份一次的备份组. Advanced File Type Device (AFTD) 为大容量磁盘设备和并发操作设计的磁盘备份设备配置选项. A ...

  4. cmd命令查看端口和进程信息

    在我们进行WEB开发时,往往会遇到socket连接到服务器出现无法响应的问题,归根结底就是网络通讯问题,或者端口未开启的问题,下面总结了一下找出原因的方法 1 看与服务器的连接:ping ip地址

  5. POJ 3264 Balanced Lineup(RMQ)

    点我看题目 题意 :N头奶牛,Q次询问,然后给你每一头奶牛的身高,每一次询问都给你两个数,x y,代表着从x位置上的奶牛到y位置上的奶牛身高最高的和最矮的相差多少. 思路 : 刚好符合RMQ的那个求区 ...

  6. android对大图片的缓存处理

    废话不多说,直接上代码 package com.huge.emj.common.util; import java.io.File; import java.io.FileInputStream; i ...

  7. windows phone 中的TextBlock的一些特性(TextWrapping,TextWrapping)

    文字很长时,换行:TextWrapping="Wrap" 文字很长时,省略:TextWrapping="WordEllipsis"

  8. asp中将文本框内的日期转换成datetime类型的数据

    将字符类型的日期转化为DateTime类型主要有以下方法: 方法一:Convert.ToDateTime(string) string格式有要求,必须是yyyy-MM-dd hh:mm:ss 方法二: ...

  9. Android笔记5-与USB HID 设备通信(一)

    1.了解 支持USB 主机(host)或者从机(accessary )模式最终是取决于设备的硬件,而与平台版本无关.我们可以通过usesfeature这个方法来查询自己的设备是否支持USB主从.   ...

  10. Android开发中一些被冷落但却很有用的类和方法

    MediaMetadataRetriever 顾名思义,就是用来获取媒体文件一些相关信息的类.包括一首歌的标题,作者,专辑封面和名称,时长,比特率等等.如果是视频的话,可以获取视频的长宽,预览图. h ...