项目命名: JPhysicalEngine
项目目的: 自己爱好/毕业设计
项目人员:
运行机器: Intel P8600 2.4GHz、2G内存、Intel GMA X4500 HD
开发环境: Linux Ubuntu 10.10
开发语言: Java
开发工具: Eclipse
项目描述: 使用Java开发的2D游戏物理引擎,可以使得以后开发类似愤怒的小鸟、雷电等物理、碰撞、动作类游戏可以更快速、更方便。
项目进度:
【已实现】
版本 完成日期 实现功能
V0.1 [2011-04-07] 大致框架
V0.2 [2011-04-11] 基本动画
V0.3 [2011-04-15] 恒力和AABB碰撞检测
V0.4 [2011-04-22] 框架优化、简单粒子系统
V0.5 [2011-05-05] 更精确的OBB碰撞检测
【待实现】
版本 计划完成日期 实现功能
V0.6 [2011-05-10] 变力
V0.7 [2011-05-12] 更完善的粒子系统
V0.8 [2011-05-17] 弹力、引力
V0.9 [2011-05-20] 框架优化、简单的输入输出操作
赶紧弄了0.5版本。修改了碰撞底层的实现。
这个DEMO和上一个DEMO 的区别:
1、从updateWorld里面的碰撞检测看到,这个检测很方便。
2、底层判断已经使用了SAT判断。
3、修改了按键部分的BUG。
4、代码依然很少。
目前引用自制引擎做的DEMO2:
引入了引擎的DEMO代码:
/**************************************************************************** * org.ubird.demo.Demo2.java * * 该类属于JPhysicalEngine游戏引擎中的一部分。JPhysicalEngine游戏引擎 * 使用Java开发,是一款可以免费学习使用的2D游戏引擎。 * **************************************************************************** * Apache Licence 2.0 **************************************************************************** * * Copyright 2011 蔡俊鸿 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. **************************************************************************** * * 创建于:2011-4-23 * @since JDK1.6 * ****************************************************************************/ package org.ubird.demo; import java.awt.Image; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.io.IOException; import javax.imageio.ImageIO; import org.ubird.app.SimpleGame; import org.ubird.collision.CollisionDetector; import org.ubird.collision.CollisionResult; import org.ubird.scene.node.ImageNode; import org.ubird.scene.node.Node; import org.ubird.scene.particle.ParticleNode; import org.ubird.scene.particle.ParticleSystem; import org.ubird.sound.SoundManager; /** * 测试类2 * @author junhong.c * @version 1.0 * @version 1.1 [2011-05-05] 调整了碰撞检测部分的代码 * @version 1.1 [2011-05-05] 增加了按V可以显示物体的轴的功能 * @version 1.1 [2011-05-05] 增加了按B可以显示物体的边界的功能 * @date 2011-4-23 */ public class Demo2 extends SimpleGame{ private Node player; private ParticleNode bombParticle = ParticleSystem.getParticles(500,15,15,10); private ParticleNode hitParticle = ParticleSystem.getParticles(500,15,15,10); private ImageNode[] bullets = new ImageNode[40]; private int currentBullet = 0; private long delay; //子弹延时 private boolean fire; private boolean fire2; private boolean fire3; private boolean down; private boolean up; private boolean right; private boolean left; private Node[] enemys = new ImageNode[7]; private Thread keyProcessThread; public Demo2() { super("Java2D游戏引擎 V0.5.0 -- By John.Cha"); } public static void main(String[] args) throws IOException { SimpleGame game = new Demo2(); game.setFPS(60); game.setStageBackground("resources/bg.png"); game.setStageSize(1000,600); game.start(); } @Override public void initWorld() { initScene(); initNode(); initEvent(); } /** * 事件 */ private void initEvent() { addKeyListener(new MyKeyListener()); } /** * 定义自己的事件监听 * @author junhong.c * @version 1.0 * @date 2011-4-22 */ private class MyKeyListener implements KeyListener{ @Override public void keyTyped(KeyEvent e) { if(e.getKeyChar()=='v'){ setDrawAxes(!isDrawAxes()); } if(e.getKeyChar()=='b'){ setDrawBounds(!isDrawBounds()); } } @Override public void keyReleased(KeyEvent e) { switch(e.getKeyCode()){ case KeyEvent.VK_W : up=false; break; case KeyEvent.VK_S: down=false; break; case KeyEvent.VK_D: right=false; break; case KeyEvent.VK_A: left=false; break; case KeyEvent.VK_J: fire=false; break; case KeyEvent.VK_K: fire2=false; break; case KeyEvent.VK_L: fire3=false; break; } } @Override public void keyPressed(KeyEvent e) { if(fire && e.getKeyCode()==KeyEvent.VK_A) return; if(fire2 && e.getKeyCode()==KeyEvent.VK_S) return; if(fire3 && e.getKeyCode()==KeyEvent.VK_D) return; if(up && e.getKeyCode()==KeyEvent.VK_UP) return; if(down && e.getKeyCode()==KeyEvent.VK_DOWN) return; if(left && e.getKeyCode()==KeyEvent.VK_LEFT) return; if(right && e.getKeyCode()==KeyEvent.VK_RIGHT) return; switch(e.getKeyCode()){ case KeyEvent.VK_W : up=true; break; case KeyEvent.VK_S: down=true; break; case KeyEvent.VK_D: right=true; break; case KeyEvent.VK_A: left=true; break; case KeyEvent.VK_I: bombParticle.start(player.getLocation().getIntX()-player.getWidth()/2-3,player.getLocation().getIntY()-player.getHeight()); break; case KeyEvent.VK_J: fire=true; break; case KeyEvent.VK_K: fire2=true; break; case KeyEvent.VK_L: fire3=true; break; } if(!getKeyProcessThread().isAlive()) getKeyProcessThread().start(); } } /** * 场景 */ private void initScene() { add(bombParticle); add(hitParticle); } /** * 处理按键的线程 * @return */ public Thread getKeyProcessThread(){ if(keyProcessThread==null){ keyProcessThread = new Thread(){ public void run(){ try { while(true){ Thread.sleep(getSPF()); float v = 0.2f; if(up) player.getVelocity().setY(-v); else if(down) player.getVelocity().setY(v); if(left) player.getVelocity().setX(-v); else if(right) player.getVelocity().setX(v); if(!(up||down)) player.getVelocity().setY(0); if(!(left||right)) player.getVelocity().setX(0); if( fire ){ if(delay <= System.currentTimeMillis()){ for(int i=0; i<2; i++){ bullets[currentBullet].getVelocity().setY(-v*2f); bullets[currentBullet].getVelocity().setX(0); int fix = i == 0 ? -15 : 15; bullets[currentBullet].rotate(0); float x = player.getLocation().getX()+(player.getWidth() - bullets[currentBullet].getWidth())*0.5f; bullets[currentBullet].setLocation(fix+x, player.getLocation().getY()-bullets[currentBullet].getHeight()); bullets[currentBullet].rotate(0); currentBullet = ++currentBullet%bullets.length; SoundManager.play("resources/sound/bullet.wav"); } delay = System.currentTimeMillis()+80; } } if(fire2){ if(delay <= System.currentTimeMillis()){ for(int i=0; i<3; i++){ bullets[currentBullet].getVelocity().setY(-v*2f); int fix = i == 0 ? -15 : i==2 ? 15 : 0; float vx = i==0 ? -v/4 : i==1 ? 0 : v/4; bullets[currentBullet].getVelocity().setX(vx); bullets[currentBullet].rotate(0); float x = player.getLocation().getX()+(player.getWidth() - bullets[currentBullet].getWidth())*0.5f; bullets[currentBullet].setLocation(fix+x, player.getLocation().getY()-bullets[currentBullet].getHeight()); bullets[currentBullet].rotate(0); currentBullet = ++currentBullet%bullets.length; SoundManager.play("resources/sound/bullet.wav"); } delay = System.currentTimeMillis()+100; } } if(fire3){ for(int i=0; i<bullets.length; i++){ bullets[currentBullet].getVelocity().setY(-v*2f); float shita = 6.28f*i/(bullets.length-1); bullets[currentBullet].rotate(shita); float vx = (float) (v*Math.cos(shita)); float vy = (float) (v*Math.sin(shita)); bullets[currentBullet].getVelocity().setX(vx); bullets[currentBullet].getVelocity().setY(vy); bullets[currentBullet].setLocation( player.getLocation().getX()+(player.getWidth()-bullets[currentBullet].getWidth())/2, player.getLocation().getY()+(player.getHeight()-bullets[currentBullet].getHeight())/2); bullets[currentBullet].setDelay(currentBullet); currentBullet = ++currentBullet%bullets.length; } SoundManager.play("resources/sound/bomb.wav"); fire3=false; } } } catch (InterruptedException e) { e.printStackTrace(); } } }; } return keyProcessThread; } private void initNode() { try { Image bulletImage = ImageIO.read(getClass().getClassLoader().getResource("resources/bullet.png")); Image planeImage = ImageIO.read(getClass().getClassLoader().getResource("resources/planes.png")); /* * 初始化子弹 */ for(int i=0; i<bullets.length; i++){ bullets[i] = new ImageNode(bulletImage,0,70,35,35); bullets[i].setSize(35,35); bullets[i].setLocation(-500,-500); bullets[i].setExtent(35/2, 0); add(bullets[i]); } /* * 初始化玩家 */ player = new ImageNode(planeImage,100,0,100,120); //平抛--图像结点 player.setSize(60,72); player.setLocation((getStageWidth()-player.getWidth())/2,getStageHeight()-player.getHeight()); player.setMass(10); add(player); /* * 初始化敌人 */ for(int i=0; i<enemys.length; i++){ enemys[i] = new ImageNode(planeImage,0,0,100,120); enemys[i].setSize(60,72); enemys[i].rotate(6.28f*i/(enemys.length-1)); enemys[i].setLocation(i*(enemys[i].getWidth()+70),100); add(enemys[i]); } } catch (IOException e1) { e1.printStackTrace(); } } @Override public void updateWorld(long time) { player.update(time); bombParticle.update(time); hitParticle.update(time); //更新子弹 for(int i=0; i<bullets.length; i++){ bullets[i].update(time); } //更新敌机 for(int i=0; i<enemys.length; i++){ if(enemys[i]!=null){ enemys[i].update(time); } } //子弹和敌机碰撞检测 for(int i=0; i<bullets.length; i++){ if(bullets[i]!=null){ for(int j=0; j<enemys.length; j++){ if(enemys[j]!=null){ CollisionResult cr = CollisionDetector.obb(bullets[i], enemys[j]); if(cr.isCollision()){ int x = bullets[i].getLocation().getIntX(); //TODO 其实应该是碰撞的位置 int y = bullets[i].getLocation().getIntY(); //TODO 其实应该是碰撞的位置 bullets[i].getLocation().setY(-500); hitParticle.start(x, y); } } } } } } }