作为一款开源的3D游戏引擎,jpct-ae还是不错的,文档很齐全,只是相关的教程很少很少,接触这个引擎有一个多星期了,学的挺郁闷,有些3ds模型加载不进去,又找不到相关的资料,模型贴图也是一塌糊涂,导致下面的台球桌看起来一点都不像。
这款游戏引擎在国内很小众,使用的人很少,资料也很少,找了很长时间,只有http://blog.csdn.net/Simdanfeg/article/category/817423这个还不错,只可惜作者没有再继续更新了,我只好在黑暗中行走。
本来想着做一个台球游戏,现在看来任重而道远,很困难。
本文做了两个小球之间的碰撞检测,一个小球向另一个小球撞过去,模型图片放在assets文件夹里面。
源代码和模型下载地址:http://download.csdn.net/detail/itde1/4283586
代码:MainActivity.java ,注释很详细。
package com.collsion3;
import java.io.IOException;
import java.io.InputStream;
import android.app.Activity;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.Window;
import android.view.WindowManager;
/**
* Activity类
*
* @author itde1
*
*/
public class Jpct_Collsion3Activity extends Activity {
private GLSurfaceView glView;
private MyRenderer mr = new MyRenderer();
//这里设置为public static,是因为在MyRenderer里面用到
public static boolean up = false; // 方向上下左右
public static boolean down = false;
public static boolean left = false;
public static boolean right = false;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//设置无标题
requestWindowFeature(Window.FEATURE_NO_TITLE);
//设置全屏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
// 传入Resources方法
LoadFile.loadb(getResources());
new LoadFile(getResources());
glView = new GLSurfaceView(this);
glView.setRenderer(mr);
setContentView(glView);
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
glView.onPause();
}
@Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
glView.onResume();
}
@Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) { // 按键处理,当上下左右中的一个按下时,则将相应的变量置true
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_UP:
up = true;
break;
case KeyEvent.KEYCODE_DPAD_DOWN:
down = true;
break;
case KeyEvent.KEYCODE_DPAD_LEFT:
left = true;
break;
case KeyEvent.KEYCODE_DPAD_RIGHT:
right = true;
break;
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) { //松开按键
up = false;
down = false;
left = false;
right = false;
return super.onKeyUp(keyCode, event);
}
}
//载入文件
class LoadFile {
public static Resources resource;
public static Bitmap bitmap1;
public static Bitmap bitmap2;
public static Bitmap bitmap3;
public LoadFile(Resources res) {
resource = res;
}
// 载入模型
public static InputStream loadf(String fileName) {
AssetManager am = LoadFile.resource.getAssets();
try {
return am.open(fileName);
} catch (IOException e) {
return null;
}
}
// 载入纹理图片
public static void loadb(Resources res) {
bitmap1 = BitmapFactory.decodeResource(res, R.drawable.table);
bitmap2 = BitmapFactory.decodeResource(res, R.drawable.bool1);
bitmap3=BitmapFactory.decodeResource(res, R.drawable.bool2);
}
}
MyRenderer.java:
package com.collsion3;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import com.threed.jpct.*;
import com.threed.jpct.Matrix;
import com.threed.jpct.util.MemoryHelper;
import android.opengl.*;
public class MyRenderer implements GLSurfaceView.Renderer{
// FrameBuffer对象
private FrameBuffer fb;
// World对象
private World world;
// RGBColor
private RGBColor back = new RGBColor(50, 50, 100);
// Object3D对象
private Object3D plane=null;
private Object3D ball1=null;
private Object3D ball2=null;
// SimpleVector
// 通过设置组件的x,y,z向量来创建一个SimpleVector对象 ,表示小球的运动方向和速度
private SimpleVector move = new SimpleVector(-4.0, 0.0, 4.0);
private boolean collsion=false;//是否发生碰撞
private SimpleVector tem;
// FPS
private int fps = 0;
private long time = System.currentTimeMillis();
// 默认构造
// 对该项目的一些优化
public MyRenderer() {
// 绘制的最多的Polygon数量,默认为4096,此处如果超过500,则不绘制
Config.maxPolysVisible = 500;
// 最远的合适的平面,默认为1000
Config.farPlane = 1500;
Config.glTransparencyMul = 0.1f;
Config.glTransparencyOffset = 0.1f;
// 使JPCT-AE这个引擎使用顶点而不是顶点数组缓冲对象,因为它可能会使某些硬件更快
// 但在Samsung Galaxy,它并不能工作的很好,可能使之崩溃,这就是它默认为false的原因
Config.useVBO = true;
Texture.defaultToMipmapping(true);
Texture.defaultTo4bpp(true);
}
@Override
public void onDrawFrame(GL10 gl) {
// TODO Auto-generated method stub
try {
if (true) {
move();//实现上下左右键
SimpleVector trsn = ball2.checkForCollisionSpherical(move, 10f);
//求出两个球之间距离
SimpleVector length=new SimpleVector(ball1.getTransformedCenter().x-ball2.getTransformedCenter().x,
ball1.getTransformedCenter().y-ball2.getTransformedCenter().y,ball1.getTransformedCenter().z-ball2.getTransformedCenter().z);
//判断两球之间距离是否小于两球半径之和,若小于,则发生碰撞
if(length.length()<=20+5){
collsion=true;//碰撞发生
}
//如果发生碰撞
if(collsion){
//判断小球是否已经跨越平面了
if(ball2.getTranslation(tem).z<-100||ball1.getTranslation(tem).z>100){
ball1.clearTranslation();
ball2.clearTranslation();
ball1.setVisibility(false);//球消失
ball2.setVisibility(false);
}
else{//小球设置新的运动方向
ball1.translate(new SimpleVector(1,0,3));
ball2.translate(new SimpleVector(-2,0,-2));
}
}
//或者if(!collsion)
//如果没有碰撞
else ball2.translate(trsn);
SimpleVector axis=new SimpleVector(1,0,1);//球旋转时轴的方向
ball2.rotateAxis(axis, (float)Math.toRadians(10));//实现球旋转
// 以定义好的RGBColor清屏
fb.clear(back);
// 变换和灯光所有的多 边形
world.renderScene(fb);
// 绘制由renderScene产生的场景
world.draw(fb);
// 渲染显示图像
fb.display();
// fps加1
fps+=1;
// 打印输出fps
if (System.currentTimeMillis() - time > 1000) {
System.out.println(fps + "fps");
fps = 0;
time = System.currentTimeMillis();
}
} else {
if (fb != null) {
fb.dispose();
fb = null;
}
}
} catch (Exception e) {
e.printStackTrace();
// 打印异常信息
Logger.log("Drawing thread terminated!", Logger.MESSAGE);
}
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
// TODO Auto-generated method stub
if (fb != null) {
fb = null;
}
// 新产生一个FrameBuffer对象
fb = new FrameBuffer(gl, width, height);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// TODO Auto-generated method stub
Logger.log("onCreate");
// 混合渲染
gl.glEnable(GL10.GL_BLEND);
gl.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA);
// 新建world对象
world = new World();
// 纹理
TextureManager tm = TextureManager.getInstance();
Texture texture2 = new Texture(LoadFile.bitmap1);
Texture texture3 = new Texture(LoadFile.bitmap2);
Texture texture4 = new Texture(LoadFile.bitmap3);
tm.addTexture("texture2", texture2);
tm.addTexture("texture3", texture3);
tm.addTexture("texture4",texture4);
// 初始化各3D元素
// plane = Primitives.getPlane(20, 10); // 得到平面
//也可以采用上面得到平面的方法,这里采用的是加载3ds模型
plane=loadModel("table.3ds", 4f);
plane.translate(0, -30, 20);
plane.rotateX(-(float) Math.PI / 2); // 从jpct-ae的坐标旋转到正常坐标系
plane.rotateY((float)Math.PI/2);
ball1 = Primitives.getSphere(10); // 得到球体
ball1.translate(0, -39, 0);
ball2=Primitives.getSphere(10);
ball2.translate(50, -39, -50);
//设置纹理贴图方式
plane.calcTextureWrap();
plane.setTexture("texture2");
ball1.calcTextureWrapSpherical();
ball1.setTexture("texture3");
ball2.calcTextureWrapSpherical();
ball2.setTexture("texture4");
world.addObject(plane);
world.addObject(ball1);
world.addObject(ball2);
//设置碰撞模式
ball1.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
ball2.setCollisionMode(Object3D.COLLISION_CHECK_SELF);
plane.setCollisionMode(Object3D.COLLISION_CHECK_OTHERS);
// 设置环境光
world.setAmbientLight(255, 255, 255);
//这里设置光照的地方不成功,不能显示光照,不知道为什么????????????
//设置光照
Light light=new Light(world);
light.setPosition(new SimpleVector(ball1.getTransformedCenter().x,
ball1.getTransformedCenter().y-100,ball1.getTransformedCenter().z-50));
light.setIntensity(255, 0, 0);
//以上3段代码没有效果
// 编译所有对象
world.buildAllObjects();
// Camera相关
Camera cam = world.getCamera();
cam.setPositionToCenter(ball1);
cam.align(ball1);//将相机方向对着物体的Z轴正方向
//相机绕着X轴旋转20度
cam.rotateCameraX((float) Math.toRadians(20));
cam.moveCamera(Camera.CAMERA_MOVEOUT, 250);
// 向外以移动
cam.moveCamera(Camera.CAMERA_MOVEUP, 60);
//cam.lookAt(plane.getTransformedCenter());
// 回收内存
MemoryHelper.compact();
}
// 载入模型
private Object3D loadModel(String filename, float scale) {
// 将载入的3ds文件保存到model数组中
Object3D[] model = Loader.load3DS(LoadFile.loadf(filename), scale);
// 取第一个3ds文件
Object3D o3d = new Object3D(0);
// 临时变量temp
Object3D temp = null;
// 遍历model数组
for (int i = 0; i < model.length; i++) {
// 给temp赋予model数组中的某一个
temp = model[i];
// 设置temp的中心为 origin (0,0,0)
temp.setCenter(SimpleVector.ORIGIN);
// 沿x轴旋转坐标系到正常的坐标系(jpct-ae的坐标中的y,x是反的)
temp.rotateX((float) (-.5 * Math.PI));
// 使用旋转矩阵指定此对象旋转网格的原始数据
temp.rotateMesh();
// new 一个矩阵来作为旋转矩阵
temp.setRotationMatrix(new Matrix());
// 合并o3d与temp
o3d = Object3D.mergeObjects(o3d, temp);
// 主要是为了从桌面版JPCT到android版的移徝(桌面版此处为o3d.build())
o3d.compile();
}
// 返回o3d对象
return o3d;
}
public void move() {
Camera cam=world.getCamera();
if (Jpct_Collsion3Activity.up) { // 按向上方向键
cam.moveCamera(cam.getDirection(), -2);//摄像机向里面移动
}
if (Jpct_Collsion3Activity.down) {
cam.moveCamera(cam.getDirection(), 2);//向外移动
}
if (Jpct_Collsion3Activity.left) {
plane.rotateY((float) Math.toRadians(-10));// 向左旋转
}
if (Jpct_Collsion3Activity.right) {
plane.rotateY((float) Math.toRadians(10)); // 向右旋转
}
}
}
碰撞时: