試作:ロープ
ロープアクションで使うためのロープの試作。
左右キーで揺らせる。
/**
* 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/maXQ
*/
/*
ターザンロープ試作
概要
- ロープアクションで使うためのロープの試作
操作方法
- 十字キーの左右でロープを揺らせる
アルゴリズム
- 関節の数だけ「ポイント」を用意
- それぞれのポイントに対して「速度&重力」による移動先を計算
- それぞれのポイントが一定距離になるように位置を修正
- 修正した位置を元に、速度を逆算
ゲームに使う場合の問題点
- 先端の速度が不安定
-- プレイヤーの制御がしづらいので、意図しない方向に飛んでしまいそう
-- もう少しベタな振り子運動の方が、ジャンプ方向を決定しやすいはず
その他
- 関節の位置がユーザにわかってしまう問題は、表示だけベジエにすることで解決するはず
- PointではなくVector3Dにした方が計算がしやすかったかもしれない
*/
package {
import flash.display.*;
import flash.events.*;
import flash.filters.*;
import flash.geom.*;
import flash.net.*;
import flash.system.*;
import flash.text.*;
import flash.media.*;
import flash.ui.*;
[SWF(width="465", height="465", frameRate="60", backgroundColor="0x000000")]
public class GameMain extends Sprite {
//==Const==
//表示サイズ
static public const VIEW_W:int = 465;
static public const VIEW_H:int = 465;
//ポイント数(先端と終端を含めた関節数)
static public const POINT_NUM:int = 10;
//ポイント間の距離
static public const POINT_DISTANCE:Number = VIEW_H/POINT_NUM * 0.4;
//入力による移動力
static public const MOVE_POW:Number = 400.0;
//重力
static public const GRAVITY:Number = -250.0;//50.0;
//ポイントの距離の補正の試行回数
static public const TRY_COUNT:int = 3;
//擬似空気抵抗(速度の減衰率)
static public const DEC_RATIO:Number = 0.99;
//ラインの太さ
static public const LINE_W:int = 8;
//==Var==
//ロープの各点([0]が根元)
public var m_Point:Array = new Array(POINT_NUM);
//ロープのグラフィック
public var g:Graphics;
//入力
public var m_InputL:Boolean = false;
public var m_InputR:Boolean = false;
//==Function==
//Init
public function GameMain(){
var i:int;
//ロープの描画登録
{
var shape:Shape = new Shape();
g = shape.graphics;
addChild(shape);
}
//ポイントの初期化
{
for(i = 0; i < POINT_NUM; i++){
m_Point[i] = new RopePoint();
m_Point[i].m_Pos.x = VIEW_W/2;
m_Point[i].m_Pos.y = VIEW_H/4 + i * POINT_DISTANCE;
}
}
//入力監視
{
addEventListener(
Event.ADDED_TO_STAGE,//stageに触るので、stageに登録されたあとに設定
function(e:Event):void{
//キー入力を見る
stage.addEventListener(KeyboardEvent.KEY_DOWN, OnKeyDown);
stage.addEventListener(KeyboardEvent.KEY_UP, OnKeyUp);
}
);
}
//Update
{
addEventListener(Event.ENTER_FRAME, Update);
}
}
//Input
private function OnKeyDown(event:KeyboardEvent):void{
switch(event.keyCode){
case Keyboard.LEFT: m_InputL = true; break;
case Keyboard.RIGHT:m_InputR = true; break;
}
}
private function OnKeyUp(event:KeyboardEvent):void{
switch(event.keyCode){
case Keyboard.LEFT: m_InputL = false; break;
case Keyboard.RIGHT:m_InputR = false; break;
}
}
//Update
public function Update(e:Event=null):void{
var DeltaTime:Number = 1.0 / stage.frameRate;
//入力による速度補正
Update_Input(DeltaTime);
//実際のロープの移動
Update_Move(DeltaTime);
//描画
Redraw();
}
//Update : Input
public function Update_Input(in_DeltaTime:Number):void{
var InputVal:Number = 0;
{
if(m_InputL){InputVal -= 1;}
if(m_InputR){InputVal += 1;}
}
{
/*
//先端だけだと変なふうに曲がる
m_Point[POINT_NUM-1].m_Vel.x += InputVal * MOVE_POW * in_DeltaTime;
//*/
/*
//一つ手前だと先がおいてけぼりな感じ
m_Point[POINT_NUM-2].m_Vel.x += InputVal * MOVE_POW * in_DeltaTime;
//*/
/*
//先端の2つだとそこそこそれっぽい
m_Point[POINT_NUM-1].m_Vel.x += InputVal * MOVE_POW * in_DeltaTime;
m_Point[POINT_NUM-2].m_Vel.x += InputVal * MOVE_POW * in_DeltaTime;
//*/
//*
//先端の力が大きくなるように全体にかけるとわりとそれっぽい
for(var i:int = 0; i < POINT_NUM; i++){
var ratio:Number = i/(POINT_NUM-1.0);
ratio *= ratio;//固定点付近の力をさらに少なくする
m_Point[i].m_Vel.x += InputVal * MOVE_POW * in_DeltaTime * ratio;
}
//*/
}
}
//Update : Move
public function Update_Move(in_DeltaTime:Number):void{
var i:int;
//まずは慣性(Vel)と重力(Gravity)での移動先を算出
var NextPos:Array = new Array(POINT_NUM);
{
for(i = 0; i < POINT_NUM; i++){
//想定移動先
if(i == 0){NextPos[i] = new Point(m_Point[i].m_Pos.x, m_Point[i].m_Pos.y); continue}
NextPos[i] = new Point(
(m_Point[i].m_Pos.x) + (m_Point[i].m_Vel.x * in_DeltaTime),
(m_Point[i].m_Pos.y) + (m_Point[i].m_Vel.y * in_DeltaTime) + (-GRAVITY * 0.5*in_DeltaTime*in_DeltaTime)
);
}
}
//ポイント間の距離を一定に保とうとする
{
for(var c:int = 0; c < TRY_COUNT; c++){
//for(i = 1; i < POINT_NUM; ++i)
for(i = POINT_NUM-1; i > 0; i--)
{
var SrcIndex:int = i-1;
var DstIndex:int = i;
var SrcPos:Point = NextPos[SrcIndex];
var DstPos:Point = NextPos[DstIndex];
var Center:Point = new Point(
(DstPos.x + SrcPos.x)/2,
(DstPos.y + SrcPos.y)/2
);
var Src_Dst:Point = new Point(
(DstPos.x - SrcPos.x),
(DstPos.y - SrcPos.y)
);
Src_Dst.normalize(1);
SrcPos.x = Center.x - POINT_DISTANCE/2 * Src_Dst.x;
SrcPos.y = Center.y - POINT_DISTANCE/2 * Src_Dst.y;
DstPos.x = Center.x + POINT_DISTANCE/2 * Src_Dst.x;
DstPos.y = Center.y + POINT_DISTANCE/2 * Src_Dst.y;
}
//[0]は戻す
NextPos[0].x = m_Point[0].m_Pos.x;
NextPos[0].y = m_Point[0].m_Pos.y;
}
}
//速度の再計算
{
for(i = 0; i < POINT_NUM; ++i){
//今回の移動量から逆算
m_Point[i].m_Vel.x = (NextPos[i].x - m_Point[i].m_Pos.x) / in_DeltaTime;
m_Point[i].m_Vel.y = (NextPos[i].y - m_Point[i].m_Pos.y) / in_DeltaTime;
//重力による速度補正
m_Point[i].m_Vel.y -= GRAVITY * in_DeltaTime;
//擬似空気抵抗
m_Point[i].m_Vel.x *= DEC_RATIO;
m_Point[i].m_Vel.y *= DEC_RATIO;
}
}
//位置の正式採用
{
for(i = 0; i < POINT_NUM; ++i){
m_Point[i].m_Pos = NextPos[i];
}
}
}
//Redraw
public function Redraw():void{
g.clear();
g.lineStyle(LINE_W, 0xFFFFFF, 1.0);
g.moveTo(m_Point[0].m_Pos.x, m_Point[0].m_Pos.y);
for(var i:int = 1; i < POINT_NUM; i++){
g.lineTo(m_Point[i].m_Pos.x, m_Point[i].m_Pos.y);
}
}
}
}
import flash.geom.*;
class RopePoint
{
//==Var==
public var m_Pos:Point = new Point(0,0);
public var m_Vel:Point = new Point(0,0);
}