Touch Tennis
スマホにて「左手でキャラ移動」「右手で弾道指定」を行うテニスゲーム(の試作)
・PCでも「十字キーでキャラ移動」「マウスで弾道指定」で操作可能
操作方法
・タッチ:左面
・プレイヤーの移動
・タッチ&ムーブ:右面
・ボールの軌道の設定
・右矢印:スマッシュ
・左矢印:ドロップ
・上下矢印:カーブ
・十字キー(PC)
・プレイヤーの移動
How To Play
- Touch : Left Court
- Player Move
- Touch : Right Court
- Ball Move
- Draw Right Arrow : Smash
- Draw Left Arrow : Drop
- Draw Up or Down Arrow : Curve
- Keyboard : Arrow (PC)
- Player Move
Android検証用
・https://play.google.com/store/apps/details?id=air.showohealer.game.airprototype
/**
* Copyright o_healer ( http://wonderfl.net/user/o_healer )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/z6Lz
*/
/*
「タッチテニス (Touch Tennis)」
・スマホにて「左手でキャラ移動」「右手で弾道指定」を行うテニスゲーム(の試作)
・PCでも「十字キーでキャラ移動」「マウスで弾道指定」で操作可能
操作方法
・タッチ:左面
・プレイヤーの移動
・タッチ&ムーブ:右面
・ボールの軌道の設定
・右矢印:スマッシュ
・左矢印:ドロップ
・上下矢印:カーブ
・十字キー(PC)
・プレイヤーの移動
How To Play
- Touch : Left Court
- Player Move
- Touch : Right Court
- Ball Move
- Draw Right Arrow : Smash
- Draw Left Arrow : Drop
- Draw Up or Down Arrow : Curve
- Keyboard : Arrow (PC)
- Player Move
Android検証用
・https://play.google.com/store/apps/details?id=air.showohealer.game.airprototype
メモ
・TouchEventは普通のPCだと動かない
・マルチタッチできなくてもマウス挙動として吸収してくれるかと思ったけどそんなことはなかったぜ!
*/
package {
import flash.display.*;
import flash.events.*;
import flash.filters.*;
import flash.geom.*;
import flash.net.*;
import flash.system.*;
import flash.text.*;
import flash.ui.*;
[SWF(width="465", height="465", frameRate="30", backgroundColor="0x000000")]
public class TouchTennis extends Sprite {
//==Embed (or Load)==
//画像のリソース位置
//*
//wonderfl用ロード型
static public const URL_GRAPHICS:String = "http://assets.wonderfl.net/images/related_images/2/29/29bc/29bc996559de455b90e03125bc9bcb2541b25e6f";
//*/
/*
//確認用ローカルロード型
static public const URL_GRAPHICS:String = "TouchTennis.png";
//*/
/*
//ブログ用埋め込み型
[Embed(source='TouchTennis.png')]
private static var Bitmap_Graphics: Class;
//*/
//==Const==
//画面の大きさ
static public const VIEW_W:int = 465;
static public const VIEW_H:int = 465;
//モード
static public var s_ModeIter:int = 0;
static public const MODE_GAME :int = s_ModeIter++;
static public const MODE_DISPLAY_WIN :int = s_ModeIter++;
//==Var==
//Pseudo Singleton
static public var Instance:TouchTennis;
//Layer
public var m_Layer_BG:Sprite = new Sprite();
public var m_Layer_OnBG:Sprite = new Sprite();
public var m_Layer_Shadow:Sprite = new Sprite();
public var m_Layer_Game:Sprite = new Sprite();
//BG
public var m_BaseBgBitmapData:BitmapData = new BitmapData(VIEW_W, VIEW_H, false, 0x222222);
//Character
public var m_Character:Vector.<GameObj_Character> = new Vector.<GameObj_Character>(2);
//Input
public var m_Input:Vector.<Controller> = new Vector.<Controller>(2);
//LineDrawer
public var m_BallLineDrawer:BallLineDrawer;
//Ball
public var m_Ball:GameObj_Ball;
//テキスト
public var m_Text:TextField = new TextField();
//モード
public var m_Mode:int = 0;//最初のモードから始める
public var m_ModeTimer:Number = 0;
//==Function==
//Init
public function TouchTennis():void{
//Pseudo Singleton
{
Instance = this;
}
//stageを参照できるようになってから初期化する(イベントリスナの追加や画面の大きさの取得のため)
if(stage != null){
Init();
}else{
addEventListener(
Event.ADDED_TO_STAGE,//ステージに追加されたら
function(e:Event):void{
Init();
}
);
}
}
public function Init():void{
var i:int;
//Settings
{
Multitouch.inputMode=MultitouchInputMode.TOUCH_POINT;
}
//Static Init
{
GameObj_Character.StaticInit();
}
//Layer
{
addChild(m_Layer_BG);
addChild(m_Layer_OnBG);
addChild(m_Layer_Shadow);
addChild(m_Layer_Game);
}
//Court
{
m_Layer_BG.addChild(new Bitmap(m_BaseBgBitmapData));
m_Layer_BG.addChild(new Bitmap(Court.CreateBitmapData()));
}
//Character
{
//User
{
i = 0;
m_Character[i] = new GameObj_Character();
m_Layer_Game.addChild(m_Character[i]);
m_Character[i].Init(Court.SIDE_LEFT);
var input_user:Controller_User = new Controller_User();
input_user.Init(m_Character[i], stage);
m_Input[i] = input_user;
}
//AI
{
i = 1;
m_Character[i] = new GameObj_Character();
m_Layer_Game.addChild(m_Character[i]);
m_Character[i].Init(Court.SIDE_RIGHT);
var input_ai:Controller_AI = new Controller_AI();
input_ai.Init(m_Character[i]);
m_Input[i] = input_ai;
}
}
//Ball
{
m_Ball = new GameObj_Ball();
m_Layer_Game.addChild(m_Ball);
m_Ball.SetPos(m_Character[1].m_X, m_Character[1].m_Y);
m_Ball.Reset(m_Character[1]);
}
//Ball Line Drawer
{
m_BallLineDrawer = new BallLineDrawer();
m_BallLineDrawer.m_Chara = m_Character[0];
m_Layer_OnBG.addChild(m_BallLineDrawer);
}
//Text
{
m_Text.selectable = false;
m_Text.autoSize = TextFieldAutoSize.LEFT;
m_Text.defaultTextFormat = new TextFormat('Verdana', 60, 0xFFFFFF, true);
m_Text.text = '';
m_Text.filters = [new GlowFilter(0x00FFFF,1.0, 8,8)];
addChild(m_Text);
}
//Update
{
addEventListener(Event.ENTER_FRAME, Update);
}
//OnEnd
{
addEventListener(Event.REMOVED_FROM_STAGE, Finish);
}
//画像ロード開始
{
//*
var loader:Loader = new Loader();
loader.load(new URLRequest(URL_GRAPHICS), new LoaderContext(true));//画像のロードを開始して
loader.contentLoaderInfo.addEventListener(
Event.COMPLETE,//ロードが完了したら
function(e:Event):void{
OnLoadEnd(loader.content);//初期化に入る
}
);
/*/
OnLoadEnd(new Bitmap_Graphics());
//*/
}
}
//Reset
public function Reset():void{
//Character
{
var num:int = m_Character.length;
for(var i:int = 0; i < num; ++i){
m_Character[i].Reset();
}
}
//Ball
{
m_Ball.SetPos(m_Character[1].m_X, m_Character[1].m_Y);
m_Ball.Reset(m_Character[1]);
}
//Text
{
m_Text.text = "";
}
}
//ロード終了時の処理
public function OnLoadEnd(in_Graphic:DisplayObject):void{
var mtx:Matrix = new Matrix(1,0,0,1, 0,0);
var clip_rect:Rectangle = new Rectangle(0,0, 32,32);
//Character
{
clip_rect.width = 24;
clip_rect.height = 32;
for(var i:int = 0; i < GameObj_Character.s_BitmapData.length; ++i){
mtx.ty = -32*i;
for(var j:int = 0; j < 3; ++j){
mtx.tx = -24*j;
GameObj_Character.s_BitmapData[i][j].draw(in_Graphic, mtx, null, null, clip_rect);
}
}
}
//BG
{
mtx.tx = -32*0;
mtx.ty = -32*2;
clip_rect.width = 32;
clip_rect.height = 32;
//Ori
var bmd_bg_ori:BitmapData = new BitmapData(32, 32, false, 0x000000);
bmd_bg_ori.draw(in_Graphic, mtx, null, null, clip_rect);
//Apply
var src_rect:Rectangle = new Rectangle(0,0,32,32);
var dst_point:Point = new Point(0,0);
for(var xx:int = 0; xx * 32 < VIEW_W; ++xx){
dst_point.x = xx * 32;
for(var yy:int = 0; yy * 32 < VIEW_H; ++yy){
dst_point.y = yy * 32;
m_BaseBgBitmapData.copyPixels(
bmd_bg_ori,
src_rect,
dst_point
);
}
}
}
}
//Finish
public function Finish(e:Event):void{
removeEventListener(Event.ADDED_TO_STAGE, Init);
removeEventListener(Event.ENTER_FRAME, Update);
removeEventListener(Event.REMOVED_FROM_STAGE, Finish);
//Settings
{
Multitouch.inputMode=MultitouchInputMode.NONE;
}
}
//Update
public function Update(e:Event=null):void{
var DeltaTime:Number = 1.0 / stage.frameRate;
m_ModeTimer += DeltaTime;
switch(m_Mode){
case MODE_GAME:
Update_GameObj(DeltaTime);
break;
case MODE_DISPLAY_WIN:
Update_GameObj(DeltaTime);
Check_DisplayWinEnd();
break;
}
}
//Update : GameObj
public function Update_GameObj(in_DeltaTime:Number):void{
//Character
{
var num:int = 2;
for(var i:int = 0; i < num; ++i){
//Input
{
m_Input[i].Update(in_DeltaTime);
}
//GameObj
{
m_Character[i].Update(in_DeltaTime);
}
}
}
//Ball
{
m_Ball.Update(in_DeltaTime);
}
//HitCheck
{
HitCheck();
}
//LineDrawerもここで
{
m_BallLineDrawer.Update();
}
}
//Update : HitCheck
public function HitCheck():void{
//2回以上バウンドしてたらチェックしない
if(2 <= m_Ball.m_BoundNum){
return;
}
var num:int = 2;
for(var i:int = 0; i < num; ++i){
//打った側とは接触確認しない
if(m_Ball.m_CourtSide == m_Character[i].m_CourtSide){
continue;
}
var GapX:Number = m_Character[i].m_X - m_Ball.m_X;
var GapY:Number = m_Character[i].m_Y - m_Ball.m_Y;
var Distance:Number = Math.sqrt(GapX*GapX + GapY*GapY);
var Height:Number = Math.abs(m_Ball.m_OffsetY - (m_Character[i].m_OffsetY-32/2));
if(Distance < 16/2 + 24/2 && Height <= 32/2){
m_Ball.Reset(m_Character[i]);
break;
}
}
}
//Check : DisplayWinEnd
public function Check_DisplayWinEnd():void{
const TIME:Number = 3;
if(TIME <= m_ModeTimer){
Reset();
GoToMode(MODE_GAME);
}
}
//OnBound
public function OnBound(in_BoundNum:int):void{
if(m_Mode != MODE_GAME){
return;
}
switch(in_BoundNum){
case 1://最初のバウンド
if(! Court.IsRangeIn(m_Ball)){//コートの範囲外なら
//ボールを受ける側の得点とする
AddPoint(1 - m_Ball.m_CourtSide);
}
break;
case 2://2回目のバウンド
//ボールを打った側の得点とする
AddPoint(m_Ball.m_CourtSide);
break;
}
}
//AddPoint
public function AddPoint(in_CourtSide:int):void{
m_Text.text = "Win";
if(in_CourtSide == Court.SIDE_LEFT){
m_Text.x = VIEW_W*1/4 - m_Text.textWidth/2;
}else{
m_Text.x = VIEW_W*3/4 - m_Text.textWidth/2;
}
m_Text.y = VIEW_H/2 - m_Text.textHeight/2;
GoToMode(MODE_DISPLAY_WIN);
}
//Mode
public function GoToMode(in_Mode:int):void{
m_Mode = in_Mode;
m_ModeTimer = 0;
}
}
}
import flash.display.*;
import flash.events.*;
import flash.filters.*;
import flash.geom.*;
import flash.net.*;
import flash.system.*;
import flash.ui.*;
//コートに関する情報
class Court
{
//==Const==
//Side
static public const SIDE_LEFT :int = 0;
static public const SIDE_RIGHT:int = 1;
//Coord
static public const CENTER_X:int = TouchTennis.VIEW_W/2;
static public const CENTER_Y:int = TouchTennis.VIEW_H/2;
static public const LINE_LX:int = TouchTennis.VIEW_W * 1/8;
static public const LINE_RX:int = TouchTennis.VIEW_W * 7/8;
static public const LINE_UY:int = TouchTennis.VIEW_H * 2/8;
static public const LINE_DY:int = TouchTennis.VIEW_H * 6/8;
//==Function==
//初期位置の設定
static public function SetInitPos(in_Chara:GameObj_Character):void{
if(in_Chara.GetCourtSide() == SIDE_LEFT){
//Left
in_Chara.SetPos(LINE_LX, (CENTER_Y + LINE_DY) / 2);
}else{
//Right
in_Chara.SetPos(LINE_RX, (CENTER_Y + LINE_UY) / 2);
}
}
//移動可能範囲の制限
static public function LimitPosition(in_Chara:GameObj_Character):void{
if(in_Chara.GetCourtSide() == SIDE_LEFT){
//Left
if(CENTER_X <= in_Chara.m_X){
in_Chara.m_X = CENTER_X-1;
}
}else{
//Right
if(in_Chara.m_X < CENTER_X){
in_Chara.m_X = CENTER_X;
}
}
}
//指定座標(in_X)はin_Charaのコート側か
static public function IsInOwnSide(in_Chara:GameObj_Character, in_X:Number):Boolean{
if(in_Chara.GetCourtSide() == SIDE_LEFT){
//Left
return (in_X < CENTER_X);
}else{
//Right
return (CENTER_X <= in_X);
}
}
//そのボールはコートにInしているか
static public function IsRangeIn(in_Ball:GameObj_Ball):Boolean{
var x:int = in_Ball.m_X;
var y:int = in_Ball.m_Y;
if(in_Ball.m_CourtSide == SIDE_LEFT){//左側で打たれたもの
//右側のコート内に入っているか
return (CENTER_X < x && x <= LINE_RX && LINE_UY <= y && y <= LINE_DY);
}else{//右側で打たれたもの
//左側のコート内に入っているか
return (LINE_LX <= x && x < CENTER_X && LINE_UY <= y && y <= LINE_DY);
}
}
//コート用画像の生成
static public function CreateBitmapData():BitmapData{
var bmd:BitmapData = new BitmapData(TouchTennis.VIEW_W, TouchTennis.VIEW_H, true, 0x00000000);
//Draw Line
{
var shape:Shape = new Shape();
var g:Graphics = shape.graphics;
//Outer
g.lineStyle(4, 0xFFFFFF, 0.9);
g.beginFill(0x44EE00, 0.9);
g.drawRect(LINE_LX, LINE_UY, LINE_RX - LINE_LX, LINE_DY - LINE_UY);
g.endFill();
//Center
g.moveTo(CENTER_X, LINE_UY);
g.lineTo(CENTER_X, LINE_DY);
bmd.draw(shape);
}
return bmd;
}
}
//ゲームに出てくる物体全般の基底クラス
class GameObj extends Sprite
{
//==Var==
//Coord
public var m_X:Number = 0;
public var m_Y:Number = 0;
public var m_OffsetY:Number = 0;//ボールが浮いてる時など用(ジャンプで使えなくもないが)
//Courtside
public var m_CourtSide:int = 0;
//Graphic
public var m_Layer_Obj:Sprite = new Sprite();
public var m_Layer_Shadow:Sprite = new Sprite();
//==Function==
//#Init
//Init
public function GameObj(){
addChild(m_Layer_Obj);
TouchTennis.Instance.m_Layer_Shadow.addChild(m_Layer_Shadow);
}
//#Set
//Set : Pos
public function SetPos(in_X:Number, in_Y:Number):void{
m_X = in_X;
m_Y = in_Y;
}
//PosData => GraphicPos
public function RefreshPos():void{
m_Layer_Obj.x = m_X;
m_Layer_Obj.y = m_Y + m_OffsetY;
m_Layer_Shadow.x = m_X;
m_Layer_Shadow.y = m_Y;
}
//#Update
//Update
virtual public function Update(in_DeltaTime:Number):void{
}
}
//キャラクター
class GameObj_Character extends GameObj
{
//==Const==
//表示画像
static public var s_BitmapData:Vector.<Vector.<BitmapData> > = new Vector.<Vector.<BitmapData> >(2);
//アニメーション用パラメータ
static public const ANIM_CYCLE:Number = 1.0;
static public const ANIM_ITER:Array = [0,1,2,1];
static public const ANIM_NUM:int = ANIM_ITER.length;
//==Var==
public var m_VX:Number = 0;
public var m_VY:Number = 0;
//キャラ画像は何番目を使うか
public var m_GraphicIndex:int = 0;
//画像表示
public var m_Bitmap:Bitmap;
//アニメーションの方向
public var m_AnimDir:int = 0;
//アニメーション用タイマー
public var m_AnimTimer:Number = 0.0;
//
public var m_BallSrcX:int = 0;
public var m_BallSrcY:int = 0;
public var m_BallDstX:int = 0;
public var m_BallDstY:int = 0;
public var m_ReqBallSrcX:int = 0;
public var m_ReqBallSrcY:int = 0;
public var m_ReqBallDstX:int = 0;
public var m_ReqBallDstY:int = 0;
//==Function==
//#Init
//Static Init
static public function StaticInit():void{
var num:int = s_BitmapData.length;
for(var i:int = 0; i < num; ++i){
s_BitmapData[i] = new Vector.<BitmapData>(3);
for(var j:int = 0; j < 3; ++j){
s_BitmapData[i][j] = new BitmapData(24, 32, true, 0x00000000);
}
}
}
//Init
public function Init(in_CourtSide:int):void{
//Param
{
m_CourtSide = in_CourtSide;
//仮でコートサイドによりキャラを決定
m_GraphicIndex = in_CourtSide;
}
//Pos
{
Court.SetInitPos(this);
}
//Graphic
{
//Chara
{
m_Bitmap = new Bitmap();
m_Bitmap.x = -24/2;
m_Bitmap.y = -32;
if(in_CourtSide == Court.SIDE_RIGHT){
m_Layer_Obj.scaleX = -1;
}
m_Layer_Obj.addChild(m_Bitmap);
}
//Shadow
{
const shadow_rad:int = 16;
var shape:Shape = new Shape();
var g:Graphics = shape.graphics;
var bmd_shadow:BitmapData = new BitmapData(shadow_rad, shadow_rad/2, true, 0x00000000);
g.clear();
g.lineStyle(0,0,0);
g.beginFill(0x000000, 0.8);
g.drawEllipse(0, 0, shadow_rad, shadow_rad/2);
g.endFill();
bmd_shadow.draw(shape);
var bmp_shadow:Bitmap = new Bitmap(bmd_shadow);
bmp_shadow.x = -shadow_rad/2;
bmp_shadow.y = -shadow_rad/2 / 2;
m_Layer_Shadow.addChild(bmp_shadow);
}
}
}
//Reset
public function Reset():void{
//Pos
{
Court.SetInitPos(this);
}
}
//#Set
//Set : Vel
public function SetVel(in_VX:Number, in_VY:Number):void{
m_VX = in_VX;
m_VY = in_VY;
}
//Request : Ball : Src
public function ReqBallSrcPos(in_X:int, in_Y:int):void{
m_ReqBallSrcX = in_X;
m_ReqBallSrcY = in_Y;
}
//Request : Ball : Dst
public function ReqBallDstPos(in_X:int, in_Y:int):void{
m_ReqBallDstX = in_X;
m_ReqBallDstY = in_Y;
}
//Apply : Ball
public function ApplyBallPos():void{
m_BallSrcX = m_ReqBallSrcX;
m_BallSrcY = m_ReqBallSrcY;
m_BallDstX = m_ReqBallDstX;
m_BallDstY = m_ReqBallDstY;
}
//#Get
public function GetCourtSide():int{
return m_CourtSide;
}
public function GetMaxVelocity():Number{
//一秒で何ドット進むか
return 100;//200;
}
//#Update
//Update
override public function Update(in_DeltaTime:Number):void{
Update_Move(in_DeltaTime);
Update_Anim(in_DeltaTime);
}
//Update : Move
public function Update_Move(in_DeltaTime:Number):void{
//Pos
{
//Move
m_X += m_VX * in_DeltaTime;
m_Y += m_VY * in_DeltaTime;
//Limit
Court.LimitPosition(this);
//Refresh
RefreshPos();
}
}
//Update : Anim
public function Update_Anim(in_DeltaTime:Number):void{
//m_AnimTimer
{
m_AnimTimer += in_DeltaTime;
if(m_AnimTimer > ANIM_CYCLE){m_AnimTimer -= ANIM_CYCLE;}
}
//m_AnimTimer => iter
var iter:int;
{
iter = ANIM_ITER[int(ANIM_NUM * m_AnimTimer/ANIM_CYCLE)];
}
m_Bitmap.bitmapData = s_BitmapData[m_GraphicIndex][iter];
}
}
//ボール
class GameObj_Ball extends GameObj
{
//==Const==
//基本移動速度
//- スマッシュなどで変更されたりはするが、単純な点指定でのショットはこの速度で移動する
static public const BASE_VEL:Number = 100;//300;
//==Var==
//移動時間まわり
public var m_Timer:Number = 0;
public var m_MoveTime:Number = 1;
//軌道(ベジエ)まわり
public var m_SrcX:Number = 0;
public var m_SrcY:Number = 0;
public var m_CtrX:Number = 0;
public var m_CtrY:Number = 0;
public var m_DstX:Number = 0;
public var m_DstY:Number = 0;
//軌道(高度)まわり
public var m_InitOffsetY:Number = 0;
//バウンド数
public var m_BoundNum:int = 0;
//==Function==
//#Init
public function GameObj_Ball(){
const ball_rad:int = 12;
var shape:Shape = new Shape();
var g:Graphics = shape.graphics;
//Ball
{
var bmd_ball:BitmapData = new BitmapData(ball_rad, ball_rad, true, 0x00000000);
g.clear();
g.lineStyle(0,0,0);
g.beginFill(0xFFFF00, 1.0);
g.drawCircle(ball_rad/2, ball_rad/2, ball_rad/2);
g.endFill();
bmd_ball.draw(shape);
var bmp_ball:Bitmap = new Bitmap(bmd_ball);
bmp_ball.x = -ball_rad/2;
bmp_ball.y = -ball_rad;
m_Layer_Obj.addChild(bmp_ball);
}
//Shadow
{
var bmd_shadow:BitmapData = new BitmapData(ball_rad, ball_rad/2, true, 0x00000000);
g.clear();
g.lineStyle(0,0,0);
g.beginFill(0x000000, 0.8);
g.drawEllipse(0, 0, ball_rad, ball_rad/2);
g.endFill();
bmd_shadow.draw(shape);
var bmp_shadow:Bitmap = new Bitmap(bmd_shadow);
bmp_shadow.x = -ball_rad/2;
bmp_shadow.y = -ball_rad/2 / 2;
m_Layer_Shadow.addChild(bmp_shadow);
}
}
//#Reset
public function Reset(in_Chara:GameObj_Character):void{
//Param
var IsSmash:Boolean;
var AbsGapX:Number;
{
if(in_Chara.GetCourtSide() == Court.SIDE_LEFT){
//コートの左側なら右方向への移動がスマッシュ
IsSmash = (in_Chara.m_BallSrcX < in_Chara.m_BallDstX);
}else{
//右側は逆
IsSmash = (in_Chara.m_BallDstX < in_Chara.m_BallSrcX);
}
AbsGapX = Math.abs(in_Chara.m_BallDstX - in_Chara.m_BallSrcX);
//CourtSide
m_CourtSide = in_Chara.m_CourtSide;
//
m_InitOffsetY = m_OffsetY;
}
//基本軌道設定
{
//ベジエでストレートをまず作って、それを補正する形でカーブを実現
//始点は今のボール位置
m_SrcX = this.m_X;
m_SrcY = this.m_Y;
//終点は最後にタッチした位置
m_DstX = in_Chara.m_BallDstX;
m_DstY = in_Chara.m_BallDstY;
//制御点はその中間(ストレートの設定)
m_CtrX = (m_SrcX + m_DstX) / 2;
m_CtrY = (m_SrcY + m_DstY) / 2;
//カーブの反映
m_CtrY -= in_Chara.m_BallDstY - in_Chara.m_BallSrcY;
}
//移動時間の計算
{
//「始点→終点」の距離と「始点→制御点→終点」の距離の半分くらいの距離を指定速度で移動するものと考える
var GapX_SC:Number = m_CtrX - m_SrcX;
var GapY_SC:Number = m_CtrY - m_SrcY;
var GapX_CD:Number = m_DstX - m_CtrX;
var GapY_CD:Number = m_DstY - m_CtrY;
var GapX_SD:Number = m_DstX - m_SrcX;
var GapY_SD:Number = m_DstY - m_SrcY;
var Distance_SD:Number = Math.sqrt(GapX_SD*GapX_SD + GapY_SD*GapY_SD);
var Distance_SCD:Number = Math.sqrt(GapX_SC*GapX_SC + GapY_SC*GapY_SC) + Math.sqrt(GapX_CD*GapX_CD + GapY_CD*GapY_CD);
var Distance:Number = Lerp(Distance_SD, Distance_SCD, 0.1);
m_MoveTime = Distance / BASE_VEL;
}
//スマッシュ・ドロップ補正
if(IsSmash)
{//スマッシュ
//スマッシュは移動速度が速くなる(=移動時間が短くなる)
//- X移動量が0なら通常の速度のまま、X移動量がHalfRatioDistanceになったら速度が倍になる
const HalfRatioDistance_Smash:Number = 0.3 * TouchTennis.VIEW_W;
var SmashRatio:Number = HalfRatioDistance_Smash / (HalfRatioDistance_Smash + AbsGapX);
m_MoveTime *= SmashRatio;
}
else
{//ドロップ
//制御点を終点に寄せることで減速させる
//- X移動量が0ならストレートのまま、X移動量がHalfRatioDistanceになったらCtrがDstに半分近づく
const HalfRatioDistance_Drop:Number = 0.25 * TouchTennis.VIEW_W;
var DropRatio:Number = 1 - HalfRatioDistance_Drop / (HalfRatioDistance_Drop + AbsGapX);
m_CtrX = Lerp(m_CtrX, m_DstX, DropRatio);
m_CtrY = Lerp(m_CtrY, m_DstY, DropRatio);
}
m_Timer = 0;
m_BoundNum = 0;
}
//#Update
//Update
override public function Update(in_DeltaTime:Number):void{
//*
//
const OffsetRatio:Number = -20;
//Time & Ratio
var Ratio:Number;
{
m_Timer += in_DeltaTime;
Ratio = m_Timer / m_MoveTime;
}
//移動
var bound:int = 0;
if(Ratio <= 1)
{//ベジエで移動
//XY
{
var SrcCtrX:Number = Lerp(m_SrcX, m_CtrX, Ratio);
var SrcCtrY:Number = Lerp(m_SrcY, m_CtrY, Ratio);
var CtrDstX:Number = Lerp(m_CtrX, m_DstX, Ratio);
var CtrDstY:Number = Lerp(m_CtrY, m_DstY, Ratio);
m_X = Lerp(SrcCtrX, CtrDstX, Ratio);
m_Y = Lerp(SrcCtrY, CtrDstY, Ratio);
}
//OffsetY
{
m_OffsetY = OffsetRatio * m_Timer * (m_MoveTime - m_Timer);
m_OffsetY += m_InitOffsetY * (1 - Ratio);
}
}
else
{//ベジエの終端速度に合わせて移動
//バウンド後の移動時間
var t:Number = m_Timer;
var MoveTime:Number = m_MoveTime;
{
for(bound = 0; bound < 5; ++bound){
if(MoveTime <= t){
t -= MoveTime;
MoveTime * 0.6;
}else{
break;
}
}
}
//XY
{
m_X = Lerp(m_CtrX, m_DstX, Ratio);
m_Y = Lerp(m_CtrY, m_DstY, Ratio);
}
//OffsetY
{
m_OffsetY = OffsetRatio * t * (MoveTime - t);
}
}
//移動の反映
RefreshPos();
//バウンド
for(var b:int = m_BoundNum; b < bound; ++b){
//バウンドしたらそれを伝達
TouchTennis.Instance.OnBound(b+1);
}
m_BoundNum = bound;
//*/
}
//#Util
//Lerp
static public function Lerp(in_Src:Number, in_Dst:Number, in_Ratio:Number):Number{
return (in_Src * (1 - in_Ratio)) + (in_Dst * in_Ratio);
}
}
//キャラクターを動かす処理の基底クラス
class Controller
{
//==Var==
//操作する対象
protected var m_Chara:GameObj_Character;
//==Function==
//操作する対象の設定
public function SetChara(in_Chara:GameObj_Character):void{
m_Chara = in_Chara;
}
//Update
virtual public function Update(in_DeltaTime:Number):void{
}
}
//キャラクターをタッチやキーボードで動かすクラス
class Controller_User extends Controller
{
//==Var==
//入力
//- Keyboard
public var m_InputL:Boolean = false;
public var m_InputR:Boolean = false;
public var m_InputU:Boolean = false;
public var m_InputD:Boolean = false;
//- Touch
public var m_TouchID_Chara:int = -1;
public var m_TouchID_Ball:int = -1;
//
public var m_CharaTrgX:int = 0;
public var m_CharaTrgY:int = 0;
//==Function==
//Init
public function Init(in_Chara:GameObj_Character, in_Parent:DisplayObject):void{
//m_Chara
{
SetChara(in_Chara);
}
//Touch & Keyboard
{
in_Parent.addEventListener(KeyboardEvent.KEY_DOWN, OnKeyDown);
in_Parent.addEventListener(KeyboardEvent.KEY_UP, OnKeyUp);
if(Multitouch.supportsTouchEvents){
in_Parent.addEventListener(TouchEvent.TOUCH_BEGIN, OnTouchDown);
in_Parent.addEventListener(TouchEvent.TOUCH_MOVE, OnTouchMove);
in_Parent.addEventListener(TouchEvent.TOUCH_END, OnTouchUp);
}else{
in_Parent.addEventListener(MouseEvent.MOUSE_DOWN, OnMouseDown);
in_Parent.addEventListener(MouseEvent.MOUSE_MOVE, OnMouseMove);
in_Parent.addEventListener(MouseEvent.MOUSE_UP, OnMouseUp);
}
}
//Init
{
//!!Test
m_Chara.m_BallSrcX = m_Chara.m_ReqBallSrcX = TouchTennis.VIEW_W*80/100;
m_Chara.m_BallSrcY = m_Chara.m_ReqBallSrcY = TouchTennis.VIEW_H*30/100;
m_Chara.m_BallDstX = m_Chara.m_ReqBallDstX = TouchTennis.VIEW_W*80/100;
m_Chara.m_BallDstY = m_Chara.m_ReqBallDstY = TouchTennis.VIEW_H*70/100;
}
}
//Keyboard
private function OnKeyDown(event:KeyboardEvent):void{
if(event.keyCode == Keyboard.LEFT){ m_InputL = true;}
if(event.keyCode == Keyboard.RIGHT){m_InputR = true;}
if(event.keyCode == Keyboard.UP){ m_InputU = true;}
if(event.keyCode == Keyboard.DOWN){ m_InputD = true;}
}
private function OnKeyUp(event:KeyboardEvent):void{
if(event.keyCode == Keyboard.LEFT){ m_InputL = false;}
if(event.keyCode == Keyboard.RIGHT){m_InputR = false;}
if(event.keyCode == Keyboard.UP){ m_InputU = false;}
if(event.keyCode == Keyboard.DOWN){ m_InputD = false;}
}
//Touch
//- Touch
private function OnTouchDown(e:TouchEvent):void{
OnDown(e.touchPointID, e.stageX, e.stageY);
}
private function OnTouchMove(e:TouchEvent):void{
OnMove(e.touchPointID, e.stageX, e.stageY);
}
private function OnTouchUp(e:TouchEvent):void{
OnUp(e.touchPointID, e.stageX, e.stageY);
}
//- Mouse
static public const DUMMY_MOUSE_ID:int = 0;
private function OnMouseDown(e:MouseEvent):void{
OnDown(DUMMY_MOUSE_ID, e.stageX, e.stageY);
}
private function OnMouseMove(e:MouseEvent):void{
OnMove(DUMMY_MOUSE_ID, e.stageX, e.stageY);
}
private function OnMouseUp(e:MouseEvent):void{
OnUp(DUMMY_MOUSE_ID, e.stageX, e.stageY);
}
//- Common
private function OnDown(in_ID:int, in_X:int, in_Y:int):void{
if(Court.IsInOwnSide(m_Chara, in_X)){
//プレイヤーの移動用のタッチ
if(m_TouchID_Chara < 0){//まだ他でタッチしてなければ採用
m_TouchID_Chara = in_ID;
m_CharaTrgX = in_X;
m_CharaTrgY = in_Y;
}
}else{
//ボールの軌道用のタッチ
if(m_TouchID_Ball < 0){//まだ他でタッチしてなければ採用
m_TouchID_Ball = in_ID;
m_Chara.ReqBallSrcPos(in_X, in_Y);
m_Chara.ReqBallDstPos(in_X, in_Y);
}
}
}
private function OnMove(in_ID:int, in_X:int, in_Y:int):void{
if(m_TouchID_Chara == in_ID){
m_CharaTrgX = in_X;
m_CharaTrgY = in_Y;
}
if(m_TouchID_Ball == in_ID){
m_Chara.ReqBallDstPos(in_X, in_Y);
}
}
private function OnUp(in_ID:int, in_X:int, in_Y:int):void{
if(m_TouchID_Chara == in_ID){
m_TouchID_Chara = -1;
}
if(m_TouchID_Ball == in_ID){
m_Chara.ApplyBallPos();
m_TouchID_Ball = -1;
}
}
//Update
override public function Update(in_DeltaTime:Number):void{
Update_Character(in_DeltaTime);
Update_Ball(in_DeltaTime);
}
//Update : Chara
public function Update_Character(in_DeltaTime:Number):void{
var MaxVel:Number = m_Chara.GetMaxVelocity();
var VX:Number = 0;
var VY:Number = 0;
var GapX:Number;
var GapY:Number;
var Len:Number;
//KeyBoard
{
GapX = 0;
GapY = 0;
if(m_InputL){GapX -= 1;}
if(m_InputR){GapX += 1;}
if(m_InputU){GapY -= 1;}
if(m_InputD){GapY += 1;}
Len = Math.sqrt(GapX*GapX + GapY*GapY);
if(Len <= 0){
//No Input
}else{
//Input
VX = MaxVel * GapX / Len;
VY = MaxVel * GapY / Len;
}
}
//Touch
{
if(0 <= m_TouchID_Chara){
GapX = m_CharaTrgX - m_Chara.m_X;
GapY = m_CharaTrgY - m_Chara.m_Y;
Len = Math.sqrt(GapX*GapX + GapY*GapY);
var Move:Number = MaxVel * in_DeltaTime;
if(Len < Move){
VX = MaxVel * GapX / Move;
VY = MaxVel * GapY / Move;
}else{
VX = MaxVel * GapX / Len;
VY = MaxVel * GapY / Len;
}
}
}
//Apply
{
m_Chara.SetVel(VX, VY);
}
}
//Update : Ball
public function Update_Ball(in_DeltaTime:Number):void{
}
}
//キャラクターをAIで動かすクラス
class Controller_AI extends Controller
{
//==Function==
//Init
public function Init(in_Chara:GameObj_Character):void{
//m_Chara
{
SetChara(in_Chara);
}
//Init
{
//!!Test
in_Chara.m_BallSrcX = TouchTennis.VIEW_W*35/100;
in_Chara.m_BallSrcY = TouchTennis.VIEW_H*55/100;
in_Chara.m_BallDstX = TouchTennis.VIEW_W*35/100;
in_Chara.m_BallDstY = TouchTennis.VIEW_H*55/100;
}
}
//Update
override public function Update(in_DeltaTime:Number):void{
Update_Character(in_DeltaTime);
Update_Ball(in_DeltaTime);
}
//Update : Chara
public function Update_Character(in_DeltaTime:Number):void{
var MaxVel:Number = m_Chara.GetMaxVelocity();
var VX:Number = 0;
var VY:Number = 0;
{
//移動目標
var TrgX:Number;
var TrgY:Number;
if(TouchTennis.Instance.m_Ball.m_CourtSide == m_Chara.m_CourtSide)
{//自分が打った球が移動中
/*
//→コートの中央あたりの戻る
if(m_Chara.m_CourtSide == Court.SIDE_LEFT){
TrgX = Lerp(Court.CENTER_X, Court.LINE_LX, 0.5);
}else{
TrgX = Lerp(Court.CENTER_X, Court.LINE_RX, 0.5);
}
TrgY = Court.CENTER_Y;
/*/
//ひとまず動かないでみる
TrgX = m_Chara.m_X;
TrgY = m_Chara.m_Y;
//*/
}
else
{//相手の打った球が移動中
//→着弾位置に移動
//直接着弾位置を取得(卑怯!)
TrgX = TouchTennis.Instance.m_Ball.m_DstX;
TrgY = TouchTennis.Instance.m_Ball.m_DstY;
//→できれば現在位置とベジエから着弾点を擬似予測したい
}
var GapX:Number = TrgX - m_Chara.m_X;
var GapY:Number = TrgY - m_Chara.m_Y;
var Len:Number = Math.sqrt(GapX*GapX + GapY*GapY);
var Move:Number = MaxVel * in_DeltaTime;
if(Len < Move){
VX = MaxVel * GapX / Move;
VY = MaxVel * GapY / Move;
}else{
VX = MaxVel * GapX / Len;
VY = MaxVel * GapY / Len;
}
}
//Apply
{
m_Chara.SetVel(VX, VY);
}
}
//Update : Ball
public function Update_Ball(in_DeltaTime:Number):void{
//とりあえずランダムに上下に振ってみる
if(Math.random() < 0.5){
m_Chara.m_BallSrcX = TouchTennis.VIEW_W*35/100;
m_Chara.m_BallSrcY = TouchTennis.VIEW_H*35/100;
m_Chara.m_BallDstX = TouchTennis.VIEW_W*35/100;
m_Chara.m_BallDstY = TouchTennis.VIEW_H*35/100;
}else{
m_Chara.m_BallSrcX = TouchTennis.VIEW_W*35/100;
m_Chara.m_BallSrcY = TouchTennis.VIEW_H*55/100;
m_Chara.m_BallDstX = TouchTennis.VIEW_W*35/100;
m_Chara.m_BallDstY = TouchTennis.VIEW_H*55/100;
}
}
//#Util
//Lerp
static public function Lerp(in_Src:Number, in_Dst:Number, in_Ratio:Number):Number{
return (in_Src * (1 - in_Ratio)) + (in_Dst * in_Ratio);
}
}
//タッチによる指定の可視化
class BallLineDrawer extends Sprite
{
//==Var==
//実際のグラフィック
public var m_Shape:Shape = new Shape();
public var m_Graphics:Graphics = m_Shape.graphics;
//可視化する対象のキャラ
public var m_Chara:GameObj_Character = null;
//前回の描画パラメータ
public var m_SrcX:int = 0;
public var m_SrcY:int = 0;
public var m_DstX:int = 0;
public var m_DstY:int = 0;
//==Function==
//Init
public function BallLineDrawer(){
//Graphic
{
m_Shape.filters = [new GlowFilter(0xFFFFFF, 0.8, 4,4)];
addChild(m_Shape);
}
}
//Update
public function Update():void{
//Check
{
if(m_Chara == null){
return;
}
}
//
if(m_SrcX != m_Chara.m_ReqBallSrcX || m_SrcY != m_Chara.m_ReqBallSrcY || m_DstX != m_Chara.m_ReqBallDstX || m_DstY != m_Chara.m_ReqBallDstY)
{
m_SrcX = m_Chara.m_ReqBallSrcX;
m_SrcY = m_Chara.m_ReqBallSrcY;
m_DstX = m_Chara.m_ReqBallDstX;
m_DstY = m_Chara.m_ReqBallDstY;
if(m_SrcX != m_DstX || m_SrcY != m_DstY)
{//Line
m_Graphics.clear();
m_Graphics.lineStyle(4, 0x00FFFF, 0.8);
m_Graphics.moveTo(m_SrcX, m_SrcY);
m_Graphics.lineTo(m_DstX, m_DstY);
var GapX:Number = m_DstX - m_SrcX;
var GapY:Number = m_DstY - m_SrcY;
var Len:Number = Math.sqrt(GapX*GapX + GapY*GapY);
var OffsetX:Number = 16 * GapY/Len;
var OffsetY:Number = 16 * -GapX/Len;
var OffsetX2:Number = 16 * -GapX/Len;
var OffsetY2:Number = 16 * -GapY/Len;
m_Graphics.moveTo(m_DstX, m_DstY);
m_Graphics.lineTo(m_DstX+OffsetX+OffsetX2, m_DstY+OffsetY+OffsetY2);
m_Graphics.moveTo(m_DstX, m_DstY);
m_Graphics.lineTo(m_DstX-OffsetX+OffsetX2, m_DstY-OffsetY+OffsetY2);
}
else
{//Dot
m_Graphics.clear();
m_Graphics.lineStyle(0,0,0);
m_Graphics.beginFill(0x00FFFF, 0.8);
m_Graphics.drawCircle(m_SrcX, m_SrcY, 4);
}
}
}
}