3D Soccer Game Prototype - pv3d,jiglibflash
Papervision3D + jiglibflash 을 이용한 축구게임 프로토 타입
간단하게 만들어 봤습니다. Fork를 통해 향상되고 멋진 기능을 가진 게임이 탄생하기 바랍니다.
1. click ball
2. press key w,a,s,d
author : Yongho Ji, jidolstar@gmail.com
/**
* Copyright jidolstar ( http://wonderfl.net/user/jidolstar )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/j8ly
*/
package {
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BlendMode;
import flash.display.Graphics;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageQuality;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.filters.BlurFilter;
import flash.geom.Vector3D;
import flash.system.IME;
import flash.system.IMEConversionMode;
import flash.ui.Keyboard;
import flash.ui.Mouse;
import flash.ui.MouseCursor;
import flash.utils.getTimer;
import jiglib.cof.JConfig;
import jiglib.geometry.JBox;
import jiglib.geometry.JCapsule;
import jiglib.geometry.JPlane;
import jiglib.geometry.JSphere;
import jiglib.geometry.JTerrain;
import jiglib.math.JMatrix3D;
import jiglib.physics.RigidBody;
import jiglib.plugin.papervision3d.Papervision3DPhysics;
import jiglib.plugin.papervision3d.Pv3dMesh;
import jiglib.plugin.papervision3d.pv3dTerrain;
import net.hires.debug.Stats;
import org.papervision3d.Papervision3D;
import org.papervision3d.core.utils.InteractiveSceneManager;
import org.papervision3d.core.utils.Mouse3D;
import org.papervision3d.events.FileLoadEvent;
import org.papervision3d.events.InteractiveScene3DEvent;
import org.papervision3d.lights.PointLight3D;
import org.papervision3d.materials.BitmapMaterial;
import org.papervision3d.materials.ColorMaterial;
import org.papervision3d.materials.MovieAssetMaterial;
import org.papervision3d.materials.MovieMaterial;
import org.papervision3d.materials.WireframeMaterial;
import org.papervision3d.materials.shadematerials.FlatShadeMaterial;
import org.papervision3d.materials.shaders.FlatShader;
import org.papervision3d.materials.shaders.GouraudShader;
import org.papervision3d.materials.shaders.LightShader;
import org.papervision3d.materials.shaders.PhongShader;
import org.papervision3d.materials.shaders.ShadedMaterial;
import org.papervision3d.materials.special.FogMaterial;
import org.papervision3d.materials.utils.MaterialsList;
import org.papervision3d.objects.DisplayObject3D;
import org.papervision3d.objects.parsers.Collada;
import org.papervision3d.objects.parsers.DAE;
import org.papervision3d.objects.primitives.Cube;
import org.papervision3d.objects.primitives.Cylinder;
import org.papervision3d.objects.primitives.Plane;
import org.papervision3d.view.BasicView;
import org.papervision3d.view.layer.ViewportLayer;
import org.papervision3d.view.layer.util.ViewportLayerSortMode;
[SWF(width="465", height="465", backgroundColor="#000000", frameRate="24")]
/**
* Papervision3D + jiglibflash 을 이용한 축구게임 프로토 타입
* 간단하게 만들어 봤습니다. Fork를 통해 향상되고 멋진 기능을 가진 게임이 탄생하기 바랍니다.
* 1. click ball
* 2. press key w,a,s,d
*
* author : Yongho Ji, jidolstar@gmail.com
*/
public class SoccerGamePrototype extends BasicView {
private var _physics:Papervision3DPhysics;
private var _light:PointLight3D;
private var _vpBallLayer:ViewportLayer;
private var _ground:Plane;
private var _ball:RigidBody;
private var _ballRadius:Number = 30;
private var _isShooting:Boolean = false;
private var _isIniting:Boolean = false;
private var _firstTime:Number;
private var _ballX:Number;
private var _windForce:Vector3D;
private var _keyForward:Boolean=false;
private var _keyBackward:Boolean=false;
private var _keyLeft:Boolean=false;
private var _keyRight:Boolean=false;
/**
* 생성자
*/
public function SoccerGamePrototype() {
super(465, 465, true, true, "Target");
configStage();
configScene();
initPhysics();
createBall();
createGround();
createGoalNet();
createGoalPost();
createBox();
startRendering();
addEventHandler();
addChild(new Stats);
}
/**
* 스테이지 환경 설정
*/
private function configStage():void {
stage.scaleMode=StageScaleMode.NO_SCALE;
stage.align=StageAlign.TOP_LEFT;
stage.quality=StageQuality.BEST;
stage.frameRate = 24;
stage.showDefaultContextMenu = false;
}
/**
* 이벤트 핸들러 함수 등록
*/
private function addEventHandler():void {
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler);
stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler);
}
/**
* 3D 환경 기본 설정
*/
private function configScene():void {
viewport.containerSprite.sortMode=ViewportLayerSortMode.INDEX_SORT;
viewport.containerSprite.addLayer(_vpBallLayer=new ViewportLayer(viewport, null));
_vpBallLayer.sortMode=ViewportLayerSortMode.Z_SORT;
camera.x=-500;
camera.y=200;
camera.z=0;
_light=new PointLight3D(true, false);
_light.x=-400;
_light.y=300;
_light.z=-100;
}
/**
* 물리엔진 기본 설정
*/
private function initPhysics():void {
_physics=new Papervision3DPhysics(scene, 10);
}
/**
* 바닥 생성
*/
private function createGround():void {
_ground = new Plane( new WireframeMaterial(0xff0000), 1000, 1000, 10, 10 );
scene.addChild(_ground);
var jGround:JPlane = new JPlane(new Pv3dMesh(_ground));
jGround.setOrientation(JMatrix3D.getRotationMatrixAxis(90));
_physics.addBody(jGround);
jGround.y = 0;
jGround.friction = 10;
}
/**
* 그물망 생성
*/
private function createGoalNet():void {
var i:Number, dx:Number = 15, dy:Number = 15, dd:Number = 15;
var w:Number = 600, h:Number = 300, d:Number = 100;
var movie : Sprite = new Sprite();
var g:Graphics = movie.graphics;
var jGoalNet:RigidBody;
var material:MovieMaterial,materials:MaterialsList;
//Back
g.lineStyle(3,0xaaaaaa,2);
for( i = 0; i <= w; i+=dx ) {
g.moveTo(i,0);
g.lineTo(i,h);
}
for( i = 0; i <= h; i+=dy ) {
g.moveTo(0,i);
g.lineTo(w,i);
}
material = new MovieMaterial(movie,true,false,false,null);
material.oneSide = false;
materials=new MaterialsList();
materials.addMaterial(material, "back");
jGoalNet = _physics.createCube( materials, w, 100, h, 5, 1, 5, 0, 0 );
jGoalNet.movable = false;
jGoalNet.rotationY = 90;
jGoalNet.material.restitution = 0.01;
jGoalNet.moveTo(new Vector3D(600+50,150,0,0));
//Top
movie = new Sprite;
g = movie.graphics;
g.lineStyle(3,0xaaaaaa,2);
for( i = 0; i <= w; i+=dx ) {
g.moveTo(i,0);
g.lineTo(i,d);
}
for( i = 0; i <= d; i+=dd ) {
g.moveTo(0,i);
g.lineTo(w,i);
}
jGoalNet = _physics.createCube( materials, w, 1, d, 3, 1, 1, 0, 0 );
jGoalNet.movable = false;
jGoalNet.rotationY = 90;
jGoalNet.rotationZ = 90;
jGoalNet.material.restitution = 0.01;
jGoalNet.moveTo(new Vector3D(550,300,0,0));
//Left
movie = new Sprite;
g = movie.graphics;
g.lineStyle(3,0xaaaaaa,2);
for( i = 0; i <= d; i+=dd ) {
g.moveTo(i,0);
g.lineTo(i,h);
}
for( i = 0; i <= h; i+=dy ) {
g.moveTo(0,i);
g.lineTo(d,i);
}
jGoalNet = _physics.createCube( materials, d, 1, h, 1, 1, 1, 0, 0 );
jGoalNet.movable = false;
jGoalNet.material.restitution = 0.01;
jGoalNet.rotationY = -180;
jGoalNet.moveTo(new Vector3D(550,h/2,-w/2,0));
//Right
jGoalNet = _physics.createCube( materials, d, 1, h, 1, 1, 1, 0, 0 );
jGoalNet.movable = false;
jGoalNet.material.restitution = 0.01;
jGoalNet.moveTo(new Vector3D(550,h/2,w/2,0));
}
/**
* 볼 포스트 생성 - Cylinder 3개로 생성
*/
private function createGoalPost():void {
function createCapsule($radius:Number,$height:Number):JCapsule {
var material:FlatShadeMaterial = new FlatShadeMaterial(_light, 0xFFFFFF, 0x555555, 5);
var cylinder:Cylinder = new Cylinder(material,$radius,$height,15,1,-1,true,false);
var jCapsule:JCapsule = new JCapsule(new Pv3dMesh(cylinder),$radius,$height);
jCapsule.movable = false;
jCapsule.material.restitution = 0.9;
scene.addChild(cylinder);
_physics.addBody( jCapsule );
return jCapsule;
}
var jCapsule:JCapsule;
jCapsule = createCapsule(10,300);
jCapsule.moveTo(new Vector3D(500,150,-300,0));
jCapsule = createCapsule(10,300);
jCapsule.moveTo(new Vector3D(500,150,300,0));
jCapsule = createCapsule(10,610);
jCapsule.rotationX = 90;
jCapsule.moveTo(new Vector3D(500,300-5,0,0));
}
/**
* 4개의 Box들 생성
*/
private function createBox():void {
var materials:MaterialsList=new MaterialsList();
materials.addMaterial(new FlatShadeMaterial(_light, 0x00FF00, 0x555555, 5), "all");
var box:RigidBody;
for (var i:int=1; i < 5; i++) {
box=_physics.createCube(materials, 100, 100, 100, 3, 3, 3);
box.mass=1;
box.x = 300;
box.y=300 * i;
_vpBallLayer.addDisplayObject3D(_physics.getMesh(box));
}
}
/**
* 축구공 생성
*/
private function createBall():void {
var material:FlatShadeMaterial = new FlatShadeMaterial(_light, 0xFFFFFF, 0x555555, 105);
_ball=_physics.createSphere(material, _ballRadius, 10, 8);
material.interactive = true;
_vpBallLayer.addDisplayObject3D(_physics.getMesh(_ball));
_ball.x=-300;
_ball.y=30;
_ball.mass=1;
_ball.friction=10;
var mesh:DisplayObject3D = _physics.getMesh(_ball);
mesh.addEventListener(InteractiveScene3DEvent.OBJECT_PRESS, press);
//공을 누른다.
function press($e:InteractiveScene3DEvent=null):void {
mesh.removeEventListener(InteractiveScene3DEvent.OBJECT_PRESS, press );
mesh.addEventListener(InteractiveScene3DEvent.OBJECT_RELEASE, release );
}
//공을 찬다!
function release($e:InteractiveScene3DEvent=null):void {
mesh.addEventListener(InteractiveScene3DEvent.OBJECT_PRESS, press );
mesh.removeEventListener(InteractiveScene3DEvent.OBJECT_RELEASE, release );
shootBall();
}
}
/**
* 축구공을 찬다.
*/
private function shootBall():void {
_isShooting = true;
_ballX = _ball.x;
_firstTime = getTimer();
var mesh:DisplayObject3D = Pv3dMesh(_ball.skin).mesh;
mesh.calculateScreenCoords(camera); //공의 스크린 좌표 계산. 3D->2D
var kickPower:Number = Math.random()*500+200;
var kickPosZTheta:Number = Math.tan( (stage.mouseX-mesh.screen.x-viewport.width/2)/_ballRadius )
var kickPosYTheta:Number = Math.tan( (stage.mouseY-mesh.screen.y-viewport.height/2)/_ballRadius );
var kickPowerX:Number = kickPower * Math.cos(kickPosYTheta)*Math.cos(kickPosZTheta);
var kickPowerY:Number = kickPower * Math.sin(kickPosYTheta);
var kickPowerZ:Number = kickPower * Math.cos(kickPosYTheta)*Math.sin(kickPosZTheta);
_ball.addWorldForce(new Vector3D(kickPowerX,kickPowerY,kickPowerZ),_ball.currentState.position);
_ball.addBodyTorque(new Vector3D(0, 0, 0));
_windForce = new Vector3D( 0, 0, Math.random()*10-5);
}
/**
* 3D 렌더링, 물리엔진 가동, 키보드에 따른 카메라 이동, 축구공 액션
*/
protected override function onRenderTick(event:Event=null):void {
_physics.step();
if( _isShooting ) {
_ball.addWorldForce(_windForce,_ball.currentState.position);
if( _ball.x > 500 || _ball.x < _ballX || getTimer() - _firstTime > 2000 ) {
_isShooting = false;
_isIniting = true;
_firstTime = getTimer();
}
_ballX = _ball.x;
}
if( _isIniting ) {
if( getTimer() - _firstTime > 2000 ) {
_isIniting = false;
_ball.x=-300;
_ball.y=30;
_ball.z = 0;
_ball.setVelocity( new Vector3D(0,0,0) );
}
}
moveCamera();
super.onRenderTick(event);
}
/**
* 카메라 이동
*/
private function moveCamera():void {
var dist:Number = 30;
if (_keyForward) {
camera.moveForward(dist);
}
if (_keyBackward) {
camera.moveBackward(dist);
}
if (_keyLeft) {
camera.moveLeft(dist);
}
if (_keyRight) {
camera.moveRight(dist);
}
}
/**
* 키보드 Down 핸들러
*/
private function keyDownHandler(e:KeyboardEvent):void {
if (e.keyCode == 229) {
IME.conversionMode=IMEConversionMode.ALPHANUMERIC_HALF;
}
switch (e.keyCode) {
case 87:
_keyForward=true;
break;
case 83:
_keyBackward=true;
break;
case 65:
_keyLeft=true;
break;
case 68:
_keyRight=true;
break;
}
}
/**
* 키보드 Up 핸들러
*/
private function keyUpHandler(e:KeyboardEvent):void {
switch (e.keyCode) {
case 87:
_keyForward=false;
break;
case 83:
_keyBackward=false;
break;
case 65:
_keyLeft=false;
break;
case 68:
_keyRight=false;
break;
}
}
}
}