package { import flash.display.*; import flash.events.*; import flash.geom.Matrix; import flash.geom.Point; import flash.geom.Rectangle; import flash.text.TextField; import flash.text.TextFormat; import flash.utils.getTimer; import flash.ui.Keyboard; //Papervision3Dのインポート import org.papervision3d.cameras.*; import org.papervision3d.render.BasicRenderEngine; import org.papervision3d.scenes.Scene3D; import org.papervision3d.view.Viewport3D; import org.papervision3d.core.*; import org.papervision3d.objects.primitives.Plane; import org.papervision3d.objects.parsers.Collada; import org.papervision3d.materials.*; import org.papervision3d.materials.special.CompositeMaterial; [SWF(backgroundColor=0x000000)] public class Main extends Sprite { // _______________________________________________________________________ // 変数宣言 /*システム系------------------------------------*/ //キー入力フラグ private var keyPressSpace:Boolean; private var keyPressB:Boolean; private var keyPressDown:Boolean; private var keyPressLeft:Boolean; private var keyPressRight:Boolean; //デバッグ用 private var debugText:Array; private var frameCount:Number; private var prevTimer:Number; /*挙動エンジン----------------------------------*/ //自機設定 private var myPositionX:Number; private var myPositionY:Number; private var myDirection:Number; //入力値(タイムラグのあるもの) private var myInputAccel:Number; private var myFullAccel:Number; private var myInputSteer:Number; private var myFullSteer:Number; //挙動パラメータ private var myMaxSpeed:Number; private var myCurrentSpeed:Number; private var myBasicAccel:Number; private var myCurrentAccel:Number; private var myBasicRotation:Number; private var myCurrentRotation:Number; private var myPitch:Number; private var myYaw:Number; //コースデータ private var groundData:Array; //現在座標の地形情報 private var groundState:int; /*描画系----------------------------------------*/ //テクスチャ private var fieldBitmap:Bitmap; private var floorFarBitmapData:BitmapData; private var floorMiddleBitmapData:BitmapData; private var floorNearBitmapData:BitmapData; //コースと描画範囲 private var fieldWidth:Number; private var fieldHeight:Number; private var floorFarWidth:Number; private var floorFarHeight:Number; private var floorMiddleWidth:Number; private var floorMiddleHeight:Number; private var floorNearWidth:Number; private var floorNearHeight:Number; //2D描画 private var viewContainer:Sprite; private var viewContainerWidth:Number; private var viewContainerHeight:Number; private var skyBitmap:Bitmap; private var fieldShape:Shape; private var fogSprite:Sprite; private var carInsideBitmap:Bitmap; //Papervision3D private var scene:Scene3D; private var scene2:Scene3D; private var viewport:Viewport3D; private var viewport2:Viewport3D; private var camera:FrustumCamera3D; private var camera2:FrustumCamera3D; private var renderer:BasicRenderEngine; private var renderer2:BasicRenderEngine; private var floorFarPlane:Plane; private var floorMiddlePlane:Plane; private var floorNearPlane:Plane; private var courseObjects:Collada; private var floorFarMaterial:BitmapMaterial; private var floorMiddleMaterial:BitmapMaterial; private var floorNearMaterial:BitmapMaterial; /*暫定------------------------------------------*/ private var bumpCount1:Number; private var bumpCount2:Number; // _______________________________________________________________________ // コンストラクタ public function Main() { initializeFlash(); initializeGame(); initialize3D(); initialize2D(); } private function initializeFlash():void { stage.frameRate = 30; //stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; stage.quality = StageQuality.LOW; } private function initializeGame():void { //描画設定 fieldWidth = 2000; //あれ、使ってない? fieldHeight = 1500; floorFarWidth = 480; floorFarHeight = 330; floorMiddleWidth = 180; floorMiddleHeight = 60; floorNearWidth = 90; floorNearHeight = 30; //自機設定 myPositionX = 810; myPositionY = 314; myDirection = 0; //右向き //運転入力値 myInputAccel = 0; myFullAccel = 100; myInputSteer = 0; myFullSteer = 100; //挙動パラメータ myMaxSpeed = 100; myCurrentSpeed = 0; myBasicAccel = 0.8; myCurrentAccel = 0; myBasicRotation = 2.2 / 180 * Math.PI; myCurrentRotation = 0; myPitch = 0; myYaw = 0; //キー入力フラグ keyPressSpace = false; keyPressB = false; keyPressDown = false; keyPressLeft = false; keyPressRight = false; //FPS算出用 frameCount = 0; prevTimer = getTimer(); //暫定 bumpCount1 = 0; bumpCount2 = 0; //視界描画範囲の設定 viewContainer = new Sprite(); viewContainerWidth = 720; viewContainerHeight = 430; viewContainer.x = stage.stageWidth / 2; viewContainer.y = stage.stageHeight / 2 + 10; //背景の作成 addChild(viewContainer); skyBitmap = new Bitmap(); skyBitmap.bitmapData = new skySample(0, 0); skyBitmap.x = -viewContainerWidth / 2; skyBitmap.y = -viewContainerHeight / 2; viewContainer.addChild(skyBitmap); fieldShape = new Shape(); var fieldGraphics:Graphics = fieldShape.graphics; fieldGraphics.beginFill(0xa2c47d); fieldGraphics.drawRect(0, 218, viewContainerWidth, viewContainerHeight - 218); //165 fieldGraphics.endFill(); fieldShape.x = -viewContainerWidth / 2; fieldShape.y = -viewContainerHeight / 2; viewContainer.addChild(fieldShape); //コースデータの作成 groundData = new Array(); //1/5サイズ var groundBitmapData:BitmapData = new courseData01(0, 0); for (var i:int = 0; i < 560; i++) { groundData[i] = new Array(); for (var j:int = 0; j < 420; j++) { if (groundBitmapData.getPixel(i, j) == 0xffffff) { groundData[i][j] = 1; } else if (groundBitmapData.getPixel(i, j) == 0x000000) { groundData[i][j] = 0; } } } //イベントリスナ addEventListener(Event.ENTER_FRAME, onEnterFrame); stage.addEventListener(KeyboardEvent.KEY_DOWN, keyDownHandler); stage.addEventListener(KeyboardEvent.KEY_UP, keyUpHandler); } private function initialize2D():void { //デバッグ表示 debugText = new Array(); var debugTextFormat = new TextFormat(); debugTextFormat.size = 11; debugTextFormat.color = 0x000000; debugTextFormat.font = "Courier New"; for (var i:int = 0; i < 16; i++) { debugText[i] = new TextField(); debugText[i].type = "dynamic"; debugText[i].width = 150; debugText[i].height = 20; debugText[i].x = 12; debugText[i].y = 8 + 15 * i; debugText[i].defaultTextFormat = debugTextFormat; debugText[i].text = " ---------"; addChild(debugText[i]); } //自機の描画 carInsideBitmap = new Bitmap(); carInsideBitmap.bitmapData = new carInside(0, 0); carInsideBitmap.x = 0; carInsideBitmap.y = 0; addChild(carInsideBitmap); } private function initialize3D():void { //2Dフォグ fogSprite = new Sprite(); var fogShape = new Shape(); //グラデーション範囲をコントロールできなかったため二重構造 var gradientGrapics:Graphics = fogShape.graphics; var gradientMatrix:Matrix = new Matrix(); var gradientColors:Array = new Array(0xddeeee, 0xddeeee, 0xddeeee, 0xddeeee); var gradientAlphas:Array = new Array(0.5, 0.3, 0.1, 0); var gradientRatios:Array = new Array(0, 25, 70, 127); gradientMatrix.createGradientBox(viewContainerWidth, viewContainerHeight - 218, Math.PI / 2, 0, 0); gradientGrapics.beginGradientFill(GradientType.LINEAR, gradientColors, gradientAlphas, gradientRatios, gradientMatrix); gradientGrapics.drawRect(0, 0, viewContainerWidth, viewContainerHeight - 218); gradientGrapics.endFill(); fogShape.y = 218; fogSprite.addChild(fogShape); fogSprite.x = -viewContainerWidth / 2; fogSprite.y = -viewContainerHeight / 2; //ビューポート設定 viewport = new Viewport3D(viewContainerWidth, viewContainerHeight, false); viewport.x = -viewContainerWidth / 2; viewport.y = -viewContainerHeight / 2; viewport2 = new Viewport3D(viewContainerWidth, viewContainerHeight, false); viewport2.x = -viewContainerWidth / 2; viewport2.y = -viewContainerHeight / 2; //描画 viewContainer.addChild(viewport); viewContainer.addChild(fogSprite); viewContainer.addChild(viewport2); //レンダー設定 renderer = new BasicRenderEngine(); renderer2 = new BasicRenderEngine(); //カメラ設定 camera = new FrustumCamera3D(viewport, 55, 10, 16000); camera.y = 250; camera.z = -400; camera2 = new FrustumCamera3D(viewport2, 55, 10, 23000); camera2.y = 250; camera2.z = myPositionX * (12000 / 360) - 400; camera2.x = myPositionY * (12000 / 360); //シーン設定 scene = new Scene3D(); scene2 = new Scene3D(); //フィールド生成 fieldBitmap = new Bitmap(); fieldBitmap.bitmapData = new course01(0, 0); //ビットマップデータの取得 var floorFarRect:Rectangle = new Rectangle( -floorFarWidth / 2, -(floorFarHeight + floorMiddleHeight + floorNearHeight), floorFarWidth, floorFarHeight); var floorMiddleRect:Rectangle = new Rectangle( -floorMiddleWidth / 2, -(floorMiddleHeight + floorNearHeight), floorMiddleWidth, floorMiddleHeight); var floorNearRect:Rectangle = new Rectangle( -floorNearWidth / 2, -floorNearHeight, floorNearWidth, floorNearHeight); floorFarBitmapData = getFloorBitmapData(myPositionX, myPositionY, myDirection, floorFarRect); floorMiddleBitmapData = getFloorBitmapData(myPositionX, myPositionY, myDirection, floorMiddleRect); floorNearBitmapData = getFloorBitmapData(myPositionX, myPositionY, myDirection, floorNearRect); //マテリアル設定 var colorMat:ColorMaterial = new ColorMaterial( 0x006699, 1 ); var wireMat:WireframeMaterial = new WireframeMaterial( 0x0099cc ); var compoMaterial:CompositeMaterial = new CompositeMaterial(); compoMaterial.addMaterial(colorMat); compoMaterial.addMaterial(wireMat); floorFarMaterial = new BitmapMaterial(floorFarBitmapData); floorMiddleMaterial = new BitmapMaterial(floorMiddleBitmapData); floorNearMaterial = new BitmapMaterial(floorNearBitmapData); //Plane作成 var farPlaneWidth:Number = 16000; var farPlaneHeight:Number = 11000; var middlePlaneWidth:Number = 6000; var middlePlaneHeight:Number = 2000; var nearPlaneWidth:Number = 3000; var nearPlaneHeight:Number = 1000; var farPlaneSegmentW:Number = 17; var farPlaneSegmentH:Number = 11; var middlePlaneSegmentW:Number = 15; var middlePlaneSegmentH:Number = 5; var nearPlaneSegmentW:Number = 18; var nearPlaneSegmentH:Number = 6; /* floorFarPlane = new Plane(compoMaterial, farPlaneWidth, farPlaneHeight, farPlaneSegmentW, farPlaneSegmentH); floorMiddlePlane = new Plane(compoMaterial, middlePlaneWidth, middlePlaneHeight, middlePlaneSegmentW, middlePlaneSegmentH); floorNearPlane = new Plane(compoMaterial, nearPlaneWidth, nearPlaneHeight, nearPlaneSegmentW, nearPlaneSegmentH); */ floorFarPlane = new Plane(floorFarMaterial, farPlaneWidth, farPlaneHeight, farPlaneSegmentW, farPlaneSegmentH); floorMiddlePlane = new Plane(floorMiddleMaterial, middlePlaneWidth, middlePlaneHeight, middlePlaneSegmentW, middlePlaneSegmentH); floorNearPlane = new Plane(floorNearMaterial, nearPlaneWidth, nearPlaneHeight, nearPlaneSegmentW, nearPlaneSegmentH); floorFarPlane.rotationX = -90; floorMiddlePlane.rotationX = -90; floorNearPlane.rotationX = -90; floorFarPlane.z = nearPlaneHeight + middlePlaneHeight + farPlaneHeight / 2; floorMiddlePlane.z = nearPlaneHeight + middlePlaneHeight / 2; floorNearPlane.z = nearPlaneHeight / 2; //3Dモデル作成 courseObjects = new Collada("courseModel01a.dae"); courseObjects.x = 35100; courseObjects.z = 46200; courseObjects.rotationY = -90; courseObjects.scale = 1/3; //3D登録 scene.addChild(floorFarPlane); scene.addChild(floorMiddlePlane); scene.addChild(floorNearPlane); scene2.addChild(courseObjects); } private function getFloorBitmapData(x:Number, y:Number, th:Number, rect:Rectangle):BitmapData { /* 概要 : 引数を元にテクスチャとして使用すべき範囲を切り取ったビットマップデータを返す * x, y : 描画基準となるキャラクタの座標 * th : 描画基準となるキャラクタの視点向き * rect : キャラクタ位置を基準に、どの範囲をテクスチャとして切り取るか * */ var returnBitmapData:BitmapData = new BitmapData(rect.width, rect.height, false, 0xa2c47d); var captureBitmapData:BitmapData = new BitmapData(rect.width, rect.height, false, 0xa2c47d); var trimRectangle:Rectangle = new Rectangle(0, 0, rect.width, rect.height); var trimMatrix:Matrix = new Matrix(); var rotateRadian:Number; var translateX:Number; var translateY:Number; //座標の回転移動 rotateRadian = -(th - (-Math.PI/2)); // -π/2の向き(上)を基準にする translateX = x * Math.cos(rotateRadian) - y * Math.sin(rotateRadian); translateY = y * Math.cos(rotateRadian) + x * Math.sin(rotateRadian); trimMatrix.rotate(rotateRadian); trimMatrix.translate( -(translateX + rect.x), -(translateY - 5 + rect.y)); //描画範囲をキャプチャ captureBitmapData.draw(fieldBitmap, trimMatrix, null, null, trimRectangle, false); returnBitmapData = captureBitmapData; return returnBitmapData; } private function onEnterFrame(event:Event):void { //挙動処理 moveMyCar(); //再描画取得、カメラ移動 moveCamera(); //3D更新 renderer.renderScene(scene, camera, viewport); renderer2.renderScene(scene2, camera2, viewport2); //デバッグ表示 renderDebug(); } private function keyDownHandler(event:KeyboardEvent):void { if (event.keyCode == Keyboard.SPACE) { keyPressSpace = true; } if (event.keyCode == 66) //[B] { keyPressB = true; } if (event.keyCode == Keyboard.DOWN) { keyPressDown = true; } if (event.keyCode == Keyboard.LEFT) { keyPressLeft = true; } if (event.keyCode == Keyboard.RIGHT) { keyPressRight = true; } if (event.keyCode == Keyboard.DELETE) { stage.fullScreenSourceRect = new Rectangle(0, 0, 700, 360); switch(stage.displayState) { case "normal": stage.displayState = "fullScreen"; break; case "fullScreen": default: stage.displayState = "normal"; break; } } } private function keyUpHandler(event:KeyboardEvent):void { if (event.keyCode == Keyboard.SPACE) { keyPressSpace = false; } if (event.keyCode == 66) //[B] { keyPressB = false; } if (event.keyCode == Keyboard.DOWN) { keyPressDown = false; } if (event.keyCode == Keyboard.LEFT) { keyPressLeft = false; } if (event.keyCode == Keyboard.RIGHT) { keyPressRight = false; } } private function moveMyCar():void { /*加減速----------------------------------------*/ //アクセルを踏む if (keyPressSpace) { myInputAccel += myFullAccel / 12; if (myInputAccel > myFullAccel) { myInputAccel = myFullAccel; } } //アクセルを放す else { if (myInputAccel >= myFullAccel / 12) { myInputAccel -= myFullAccel / 12; } else if (myInputAccel < myFullAccel / 12) { myInputAccel = 0; } } if (myInputAccel > 0) { //前進加速(低速時ほど加速が大きくなりやすい) myCurrentAccel = myBasicAccel * (myInputAccel / myFullAccel) * (0.2 + ((myMaxSpeed - myCurrentSpeed) / myMaxSpeed) * 0.8); } else if (keyPressDown && myCurrentSpeed <= 0) { //後退加速 myCurrentAccel = -myBasicAccel * 0.7; } else { //自然減速(前進中) if (myCurrentSpeed > myBasicAccel * 0.3) { myCurrentAccel = -myBasicAccel * 0.3; } //自然減速(後退中) else if (myCurrentSpeed < -myBasicAccel * 0.3) { myCurrentAccel = myBasicAccel * 0.3; } //停止 else { myCurrentAccel = 0; myCurrentSpeed = 0; } } /*旋回------------------------------------------*/ //現在速度と現在加速度による係数(0 ~ 1) var speedCoefficient:Number = (myMaxSpeed - Math.abs(myCurrentSpeed)) / myMaxSpeed; var accelCoefficient:Number; if (myCurrentSpeed > 0) { accelCoefficient = (myBasicAccel - myCurrentAccel) / (2 * myBasicAccel); } else if (myCurrentSpeed < 0) { accelCoefficient = (myBasicAccel + myCurrentAccel) / (2 * myBasicAccel); } else { accelCoefficient = 0; } //ステアを入れる if (keyPressLeft || keyPressRight) { //左ステア if (keyPressLeft) { myInputSteer -= myFullSteer / 12; if (myInputSteer < -myFullSteer) { myInputSteer = -myFullSteer; } } //右ステア if (keyPressRight) { myInputSteer += myFullSteer / 12; if (myInputSteer > myFullSteer) { myInputSteer = myFullSteer; } } } //ステアを戻す else if (!keyPressLeft && !keyPressRight) { if (myInputSteer <= -myFullSteer / 12) { myInputSteer += myFullSteer / 12; } else if (myInputSteer >= myFullSteer / 12) { myInputSteer -= myFullSteer / 12; } else { myInputSteer = 0; } } //旋回 if (myInputSteer != 0 && myCurrentSpeed != 0) { //旋回(低速時、減速時の方が曲がりやすい) myCurrentRotation = myBasicRotation * (myInputSteer / myFullSteer) * (0.3 + 0.4 * speedCoefficient + 0.3 * accelCoefficient); //低速時の旋回低下 if (myCurrentSpeed < 15 && myCurrentSpeed > 0) { myCurrentRotation = myCurrentRotation * (myCurrentSpeed / 15); } if (myCurrentSpeed > -15 && myCurrentSpeed < 0) { myCurrentRotation = myCurrentRotation * (myCurrentSpeed / -15); } } else { myCurrentRotation = 0; } //旋回による加速度低下(ステア角が大きく、高速時ほど低下する) if (!keyPressB) { myCurrentAccel -= myBasicAccel * 0.3 * Math.abs(myInputSteer / myFullSteer) * (myCurrentSpeed / myMaxSpeed); } /*ブレーキ--------------------------------------*/ //以下の減速は旋回に影響を与えない if (keyPressB) { //ブレーキ減速(前進中) if (myCurrentSpeed > myBasicAccel * 1.2) { myCurrentAccel -= myBasicAccel * 1.2; } //ブレーキ減速(後退中) else if (myCurrentSpeed < -myBasicAccel * 1.2) { myCurrentAccel += myBasicAccel * 1.2; } //停止 else { myCurrentAccel = 0; myCurrentSpeed = 0; } } /*コースアウト----------------------------------*/ //現在座標の地形情報取得 groundState = groundData[Math.floor(myPositionX / 5)][Math.floor(myPositionY / 5)]; if (groundState == 0) { if (!keyPressB) { if (myCurrentSpeed > 0) { myCurrentAccel -= myBasicAccel * 2.2 * (myCurrentSpeed / myMaxSpeed); } if (myCurrentSpeed < 0) { myCurrentAccel += myBasicAccel * 4 * (-myCurrentSpeed / myMaxSpeed); } } myCurrentRotation *= 0.7; //バンプ(暫定) if (bumpCount1 < 140) { myPitch += (0 - myPitch) / 8; } else { myPitch += (-1.6 - myPitch) / 8; } if (bumpCount2 < 500) { myYaw += 0.05; } else { myYaw -= 0.05; } bumpCount1 += Math.abs(myCurrentSpeed); bumpCount2 += Math.abs(myCurrentSpeed); if (bumpCount1 >= 210) { bumpCount1 = 0; } if (bumpCount2 >= 1000) { bumpCount2 = 0; } } /*挙動処理--------------------------------------*/ //加速処理 myCurrentSpeed += myCurrentAccel; //旋回処理 myDirection += myCurrentRotation; //速度補正(最高速) if (myCurrentSpeed > myMaxSpeed) { myCurrentSpeed = myMaxSpeed; } if (myCurrentSpeed < -myMaxSpeed * 0.3) { myCurrentSpeed = -myMaxSpeed * 0.3; } //角度補正(0 ~ 2π に収める) if (myDirection >= 2 * Math.PI) { myDirection -= 2 * Math.PI; } if (myDirection < 0) { myDirection += 2 * Math.PI; } //荷重(だいたい -1 ~ 1) myPitch += ( -(myCurrentAccel / myBasicAccel) - myPitch) / 8; myYaw += ((myCurrentRotation / myBasicRotation) - myYaw) / 8; if (Math.abs(myPitch) < 0.01) { myPitch = 0; } if (Math.abs(myYaw) < 0.01) { myYaw = 0; } } function moveCamera():void { //移動 myPositionX += 7 * (myCurrentSpeed / 100) * Math.cos(myDirection); myPositionY += 7 * (myCurrentSpeed / 100) * Math.sin(myDirection); //カメラ移動 camera2.z = myPositionX * (12000 / 360) - 400; camera2.x = myPositionY * (12000 / 360); camera2.yaw(myCurrentRotation * 180 / Math.PI); //FrustumCamera3Dはrotation系の代入が反映されないため //荷重 viewContainer.y = stage.stageHeight / 2 - 20 - (myPitch * 20); viewContainer.rotation = myYaw * 3.2; //3D描画更新 //ビットマップデータの取得 var floorFarRect:Rectangle = new Rectangle( -floorFarWidth / 2, -(floorFarHeight + floorMiddleHeight + floorNearHeight), floorFarWidth, floorFarHeight); var floorMiddleRect:Rectangle = new Rectangle( -floorMiddleWidth / 2, -(floorMiddleHeight + floorNearHeight), floorMiddleWidth, floorMiddleHeight); var floorNearRect:Rectangle = new Rectangle( -floorNearWidth / 2, -floorNearHeight, floorNearWidth, floorNearHeight); floorFarBitmapData = getFloorBitmapData(myPositionX, myPositionY, myDirection, floorFarRect); floorMiddleBitmapData = getFloorBitmapData(myPositionX, myPositionY, myDirection, floorMiddleRect); floorNearBitmapData = getFloorBitmapData(myPositionX, myPositionY, myDirection, floorNearRect); //マテリアル更新 floorFarMaterial.bitmap = floorFarBitmapData; floorMiddleMaterial.bitmap = floorMiddleBitmapData; floorNearMaterial.bitmap = floorNearBitmapData; floorFarMaterial.updateBitmap(); floorMiddleMaterial.updateBitmap(); floorNearMaterial.updateBitmap(); } function renderDebug():void { //FPS算出 frameCount += 1; if ((getTimer() - prevTimer) / 1000 >= 0.25) { var fps:Number = frameCount * 1000 / (getTimer() - prevTimer); fps = Math.floor(fps * 10) / 10; prevTimer = getTimer(); frameCount = 0; //FPS表示 debugText[0].text = "FPS : " + fps; } //デバッグ表示 debugText[2].text = "ACCEL_IN : " + Math.floor(myInputAccel); debugText[3].text = "STEER_IN : " + Math.floor(myInputSteer); debugText[5].text = "SPEED : " + Math.floor(myCurrentSpeed); debugText[6].text = "ACCEL : " + myCurrentAccel.toFixed(2); debugText[7].text = "ROTATION : " + (myCurrentRotation * 180 / Math.PI).toFixed(2); debugText[8].text = "G_PITCH : " + myPitch.toFixed(2); debugText[9].text = "G_YAW : " + myYaw.toFixed(2); debugText[11].text = "MY_X : " + Math.floor(myPositionX); debugText[12].text = "MY_Y : " + Math.floor(myPositionY); debugText[13].text = "MY_DIREC : " + Math.floor(myDirection * 180 / Math.PI); debugText[15].text = "GR_STATE : " + groundState; } } }