3Dの苦悩…
ただ見るだけのものなら3Dでも苦じゃないけど
情報が3D表現となると苦しい所が出てくる
いくらコンテンツが3Dになっても操作は2D
立体的なもののなかでは直感的な操作ができない
表現としては3Dなんだけど実質
ある軸での移動は制約をつけて
空間としては2.5次元ぐらいという
結局3D表現なしと同じみたいな
ところに落ち着くことになる
あるいは目を楽しませる以上の事はせず
3Dで情報提供するなんてことは考えないか
そうなると結局『3D表現にする意味あんの?』ってなる
3Dはユーザーが情報にたどり着くまでの
時間を長くする障害にしかならなくなる
いまのところ3Dになって意味があると思える
情報ってのは地下施設の地図とか
器具の取扱説明とかぐらい
結局空間や形状自体が情報でない限りは
3D表現は邪魔者でしかないのかもしれない
それいがいなら
目を楽しませるための飾りとしてか
さりげなく2Dの中に混じってるぐらいでいいと思う
とりあえずバリバリフル3Dのページなんて
つくる理由ってのはない
インタラクティブな3Dコンテンツを
簡単に作る為のクラスをつくろうかとも思ったけど
やっぱりそんなのは作らないほうがいいなと結論
情報が3D表現となると苦しい所が出てくる
いくらコンテンツが3Dになっても操作は2D
立体的なもののなかでは直感的な操作ができない
表現としては3Dなんだけど実質
ある軸での移動は制約をつけて
空間としては2.5次元ぐらいという
結局3D表現なしと同じみたいな
ところに落ち着くことになる
あるいは目を楽しませる以上の事はせず
3Dで情報提供するなんてことは考えないか
そうなると結局『3D表現にする意味あんの?』ってなる
3Dはユーザーが情報にたどり着くまでの
時間を長くする障害にしかならなくなる
いまのところ3Dになって意味があると思える
情報ってのは地下施設の地図とか
器具の取扱説明とかぐらい
結局空間や形状自体が情報でない限りは
3D表現は邪魔者でしかないのかもしれない
それいがいなら
目を楽しませるための飾りとしてか
さりげなく2Dの中に混じってるぐらいでいいと思う
とりあえずバリバリフル3Dのページなんて
つくる理由ってのはない
インタラクティブな3Dコンテンツを
簡単に作る為のクラスをつくろうかとも思ったけど
やっぱりそんなのは作らないほうがいいなと結論
Flashは低俗?
割と僕はついてた仕事もあってリンゴ信者なんだけど
Flashに対するappleの態度は正直気に入らない
『iPhoneアプリはレベル維持の為に
C++とかじゃないと開発させないよ』とか
iPhoneのFlashへの対応に積極的でないのはわかってたけど
あからさまに向こうが歩み寄ってくるのを
邪魔するようなマネはしないだろうと思ってた
確かにFlashはだれでも簡単にプログラミングの
まねごとができるAS1から大きくなったところがあるから
他の言語に比べて底辺が広いってのは事実かもしれない
一つまえのflashPlayerがレファラを自由にできて
とってもセキュリティー面で危なかったのも事実
だけどAS3はクラスベースの言語として成熟し
優れた開発者も多く登場してるし
もともとデザイナー寄りの言語なおかげで
デザイン面で優れたコンテンツができやすいという面もある
セキュリティーホールも改善していってる
今回ばかりはiPadディスってSlateにいっちゃうかもしれない
Slate
Flashに対するappleの態度は正直気に入らない
『iPhoneアプリはレベル維持の為に
C++とかじゃないと開発させないよ』とか
iPhoneのFlashへの対応に積極的でないのはわかってたけど
あからさまに向こうが歩み寄ってくるのを
邪魔するようなマネはしないだろうと思ってた
確かにFlashはだれでも簡単にプログラミングの
まねごとができるAS1から大きくなったところがあるから
他の言語に比べて底辺が広いってのは事実かもしれない
一つまえのflashPlayerがレファラを自由にできて
とってもセキュリティー面で危なかったのも事実
だけどAS3はクラスベースの言語として成熟し
優れた開発者も多く登場してるし
もともとデザイナー寄りの言語なおかげで
デザイン面で優れたコンテンツができやすいという面もある
セキュリティーホールも改善していってる
今回ばかりはiPadディスってSlateにいっちゃうかもしれない
Slate
simple3Dクラス
PaperVision3Dを簡単に使う為のクラスです
視点移動を簡単にできるようにするメソッドを用意してます
全ての視点移動がトゥイーンを想定していて
TweenMaxも入っていることが前提になってます
第二引数でトゥイーン時間、第三引数で遅延を
指定できるようになってます
現在は単純な視点移動しか想定していません
例えばジェットコースターのアニメーションみたいな
複雑に視点や焦点が移動する視点移動のメソッドは用意してません
■simple3Dクラス
ちょっと文字数ヤバそうなので実用例は次に
視点移動を簡単にできるようにするメソッドを用意してます
全ての視点移動がトゥイーンを想定していて
TweenMaxも入っていることが前提になってます
第二引数でトゥイーン時間、第三引数で遅延を
指定できるようになってます
現在は単純な視点移動しか想定していません
例えばジェットコースターのアニメーションみたいな
複雑に視点や焦点が移動する視点移動のメソッドは用意してません
■simple3Dクラス
/*
■使い方
トゥイーンやドラッグによる視点移動の手段を備えた
PaperVision3DのBasicViewの拡張クラスです
■パブリックプロパティ
forcus 焦点
easing トゥイーンに使用するTweenMaxのイージングの関数
onTweenComplete トゥイーン終了後に実行される関数
onFrame マウスドラッグベクトルを引数にフレーム毎に実行される関数、デフォルトは視点上下と焦点中心にカメラを回転
friction マウスドラッグベクトルに対する摩擦、1で滑りなし、デフォルトは0.1でそこそこ滑る
■パブリックメソッド
コンストラクタ(3Dオブジェクト、幅、高さ) 引数のオブジェクトを子に持つビューを生成
moveTo(座標オブジェクト、トゥイーン時間、遅延) カメラを移動
moveBy(座標オブジェクト、トゥイーン時間、遅延) カメラを移動
slideTo(座標オブジェクト、トゥイーン時間、遅延) カメラと焦点を移動
slideBy(座標オブジェクト、トゥイーン時間、遅延) カメラと焦点を移動
lookAt(座標オブジェクト、トゥイーン時間、遅延) 焦点を移動
turnTo(角度、トゥイーン時間、遅延) カメラを横回転
turnBy(角度、トゥイーン時間、遅延) カメラを横回転
rollTo(角度、トゥイーン時間、遅延) カメラを焦点を中心に縦回転
rollBy(角度、トゥイーン時間、遅延) カメラを焦点を中心に縦回転
yawTo(角度、トゥイーン時間、遅延) カメラを焦点を中心に横回転
yawBy(角度、トゥイーン時間、遅延) カメラを焦点を中心に横回転
setDistanceTo(距離、トゥイーン時間、遅延) カメラを焦点との直線上で移動
setDistanceBy(距離、トゥイーン時間、遅延) カメラを焦点との直線上で移動
*/
package simplizer.simple3D{
import flash.events.*
import flash.geom.*
import org.papervision3d.view.BasicView
import org.papervision3d.objects.DisplayObject3D
import org.papervision3d.core.math.*
import com.greensock.TweenMax
import com.greensock.easing.*
public class Simple3D extends BasicView{
private var isTweening:Boolean=false
private var isDragging:Boolean=false
private var dummyObject:Object=new Object()
public var forcus:DisplayObject3D=new DisplayObject3D()
public var easing:Function=Sine.easeInOut
public var onTweenComplete:Function=function(){}
public var onFrame:Function=function(vec){rollBy(vec.y/3);yawBy(vec.x/3)}
public var friction:Number=0.1
public function Simple3D(obj3D:DisplayObject3D,w:Number=0,h:Number=0){
super(w, h, w*h<1,true)
scene.addChild(obj3D)
camera.target=forcus
startRendering()
addEventListener(Event.ADDED_TO_STAGE,function(e){stage.addEventListener(Event.ENTER_FRAME,frameTick)})
addEventListener(Event.ADDED_TO_STAGE,function(e){stage.addEventListener(MouseEvent.MOUSE_DOWN,onDown)})
addEventListener(Event.ADDED_TO_STAGE,function(e){stage.addEventListener(MouseEvent.MOUSE_UP,onUp)})
addEventListener(Event.REMOVED_FROM_STAGE,function(e){stage.removeEventListener(MouseEvent.MOUSE_DOWN,onDown)})
addEventListener(Event.REMOVED_FROM_STAGE,function(e){stage.removeEventListener(MouseEvent.MOUSE_UP,onUp)})
var prevPnt:Point
var vec:Point=new Point()
function frameTick(e){
if(isTweening)return;
onFrame(vec)
if(isDragging){
vec=new Point(stage.mouseX,stage.mouseY).subtract(prevPnt)
prevPnt=new Point(stage.mouseX,stage.mouseY)
}else{
friction=Math.max(Math.min(friction,1),0)
vec.x*=1-friction
vec.y*=1-friction
}
}
function onDown(e){
if(isTweening)return;
if(viewport.hitTestMouse()){
isDragging=true
prevPnt=new Point(e.stageX,e.stageY)
}
}
function onUp(e){
isDragging=false
}
}
//移動
public function moveTo(pnt:Object,time:Number=0,delay:Number=0){
if(isTweening)return;
var dst:Object=new Object()
dst.x=pnt.x||((pnt.x==0)?0:camera.x)
dst.y=pnt.y||((pnt.y==0)?0:camera.y)
dst.z=pnt.z||((pnt.z==0)?0:camera.z)
if(time==0){
camera.x=dst.x
camera.y=dst.y
camera.z=dst.z
}else{
if(isDragging)return;
TweenMax.to(camera,time,{x:dst.x,y:dst.y,z:dst.z,delay:delay,onComplete:onTwnComp,ease:easing})
isTweening=true
}
}
public function moveBy(pnt:Object,time:Number=0,delay:Number=0){
if(isTweening)return;
var dst:Object=new Object()
dst.x=(pnt.x||0)+camera.x
dst.y=(pnt.y||0)+camera.y
dst.z=(pnt.z||0)+camera.z
if(time==0){
camera.x=dst.x
camera.y=dst.y
camera.z=dst.z
}else{
if(isDragging)return;
TweenMax.to(camera,time,{x:dst.x,y:dst.y,z:dst.z,delay:delay,onComplete:onTwnComp,ease:easing})
isTweening=true
}
}
public function slideTo(pnt:Object,time:Number=0,delay:Number=0){
if(isTweening)return;
var dst:Object=new Object()
var vec=Number3D.sub(camera.position,forcus.position)
dst.x=pnt.x||((pnt.x==0)?0:forcus.x)
dst.y=pnt.y||((pnt.y==0)?0:forcus.y)
dst.z=pnt.z||((pnt.z==0)?0:forcus.z)
if(time==0){
forcus.x=dst.x
forcus.y=dst.y
forcus.z=dst.z
camera.x=dst.x+vec.x
camera.y=dst.y+vec.y
camera.z=dst.z+vec.z
}else{
if(isDragging)return;
TweenMax.to(forcus,time,{x:dst.x,y:dst.y,z:dst.z,delay:delay,onComplete:onTwnComp,ease:easing})
TweenMax.to(camera,time,{x:dst.x+vec.x,y:dst.y+vec.y,z:dst.z+vec.z,delay:delay,ease:easing})
isTweening=true
}
}
public function slideBy(pnt:Object,time:Number=0,delay:Number=0){
if(isTweening)return;
var dst:Object=new Object()
var vec=Number3D.sub(camera.position,forcus.position)
dst.x=(pnt.x||0)+forcus.x
dst.y=(pnt.y||0)+forcus.y
dst.z=(pnt.z||0)+forcus.z
if(time==0){
forcus.x=dst.x
forcus.y=dst.y
forcus.z=dst.z
camera.x=dst.x+vec.x
camera.y=dst.y+vec.y
camera.z=dst.z+vec.z
}else{
if(isDragging)return;
TweenMax.to(forcus,time,{x:dst.x,y:dst.y,z:dst.z,delay:delay,onComplete:onTwnComp,ease:easing})
TweenMax.to(camera,time,{x:dst.x+vec.x,y:dst.y+vec.y,z:dst.z+vec.z,delay:delay,ease:easing})
isTweening=true
}
}
public function lookAt(pnt:Object,time:Number=0,delay:Number=0){
if(isTweening)return;
var dst:Object=new Object()
dst.x=pnt.x||((pnt.x==0)?0:forcus.x)
dst.y=pnt.y||((pnt.y==0)?0:forcus.y)
dst.z=pnt.z||((pnt.z==0)?0:forcus.z)
if(time==0){
forcus.x=dst.x
forcus.y=dst.y
forcus.z=dst.z
}else{
TweenMax.to(forcus,time,{x:dst.x,y:dst.y,z:dst.z,delay:delay,onComplete:onTwnComp,ease:easing})
isTweening=true
}
}
//回転
public function turnTo(dgr:Number,time:Number=0,delay:Number=0){
if(isTweening)return;
var dst=dgr/180*Math.PI
dummyObject=new Object()
var vec=new Point(forcus.x,forcus.z).subtract(new Point(camera.x,camera.z))
dummyObject.rad=Math.atan2(vec.y,vec.x)
dummyObject.len=vec.length
if(time==0){
dummyObject.rad=dst
update()
}else{
if(isDragging)return;
TweenMax.to(dummyObject,time,{rad:dst,delay:delay,onUpdate:update,onComplete:onTwnComp,ease:easing})
isTweening=true
}
function update(){
forcus.x=camera.x+dummyObject.len*Math.cos(dummyObject.rad)
forcus.z=camera.z+dummyObject.len*Math.sin(dummyObject.rad)
}
}
public function turnBy(dgr:Number,time:Number=0,delay:Number=0){
if(isTweening)return;
var crrPrm:Object=new Object()
var vec=new Point(forcus.x,forcus.z).subtract(new Point(camera.x,camera.z))
dummyObject.rad=Math.atan2(vec.y,vec.x)
dummyObject.len=vec.length
var dst=dgr/180*Math.PI+dummyObject.rad
if(time==0){
dummyObject.rad=dst
update()
}else{
if(isDragging)return;
TweenMax.to(dummyObject,time,{rad:dst,delay:delay,onUpdate:update,onComplete:onTwnComp,ease:easing})
isTweening=true
}
function update(){
forcus.x=camera.x+dummyObject.len*Math.cos(dummyObject.rad)
forcus.z=camera.z+dummyObject.len*Math.sin(dummyObject.rad)
}
}
public function rollTo(dgr:Number,time:Number=0,delay:Number=0){
if(isTweening)return;
var dst=dgr/180*Math.PI
dummyObject=new Object()
var vec=Number3D.sub(camera.position,forcus.position)
var baseVec=new Point(vec.x,vec.z)
var baseDrc=Math.atan2(vec.z,vec.x)
dummyObject.rad=Math.atan2(vec.y,baseVec.length)
dummyObject.len=vec.modulo
if(time==0){
dummyObject.rad=dst
update()
}else{
if(isDragging)return;
TweenMax.to(dummyObject,time,{rad:dst,delay:delay,onUpdate:update,onComplete:onTwnComp,ease:easing})
isTweening=true
}
function update(){
var rad=dummyObject.rad
while(rad>Math.PI)rad-=Math.PI*2
while(rad<-Math.PI)rad+=Math.PI*2
rad=Math.max(Math.min(rad,Math.PI*44/90),-Math.PI*44/90)
camera.y=forcus.y+dummyObject.len*Math.sin(rad)
var baseLen=dummyObject.len*Math.cos(rad)
camera.x=forcus.x+baseLen*Math.cos(baseDrc)
camera.z=forcus.z+baseLen*Math.sin(baseDrc)
}
}
public function rollBy(dgr:Number,time:Number=0,delay:Number=0){
if(isTweening)return;
dummyObject=new Object()
var vec=Number3D.sub(camera.position,forcus.position)
var baseVec=new Point(vec.x,vec.z)
var baseDrc=Math.atan2(vec.z,vec.x)
dummyObject.rad=Math.atan2(vec.y,baseVec.length)
dummyObject.len=vec.modulo
var dst=dgr/180*Math.PI+dummyObject.rad
if(time==0){
dummyObject.rad=dst
update()
}else{
if(isDragging)return;
TweenMax.to(dummyObject,time,{rad:dst,delay:delay,onUpdate:update,onComplete:onTwnComp,ease:easing})
isTweening=true
}
function update(){
var rad=dummyObject.rad
while(rad>Math.PI)rad-=Math.PI*2
while(rad<-Math.PI)rad+=Math.PI*2
rad=Math.max(Math.min(rad,Math.PI*44/90),-Math.PI*44/90)
camera.y=forcus.y+dummyObject.len*Math.sin(rad)
var baseLen=dummyObject.len*Math.cos(rad)
camera.x=forcus.x+baseLen*Math.cos(baseDrc)
camera.z=forcus.z+baseLen*Math.sin(baseDrc)
}
}
public function yawTo(dgr:Number,time:Number=0,delay:Number=0){
if(isTweening)return;
var dst=dgr/180*Math.PI
dummyObject=new Object()
var vec=new Point(camera.x,camera.z).subtract(new Point(forcus.x,forcus.z))
dummyObject.rad=Math.atan2(vec.y,vec.x)
dummyObject.len=vec.length
if(time==0){
dummyObject.rad=dst
update()
}else{
if(isDragging)return;
TweenMax.to(dummyObject,time,{rad:dst,delay:delay,onUpdate:update,onComplete:onTwnComp,ease:easing})
isTweening=true
}
function update(){
camera.x=forcus.x+dummyObject.len*Math.cos(dummyObject.rad)
camera.z=forcus.z+dummyObject.len*Math.sin(dummyObject.rad)
}
}
public function yawBy(dgr:Number,time:Number=0,delay:Number=0){
if(isTweening)return;
var crrPrm:Object=new Object()
var vec=new Point(camera.x,camera.z).subtract(new Point(forcus.x,forcus.z))
dummyObject.rad=Math.atan2(vec.y,vec.x)
dummyObject.len=vec.length
var dst=dgr/180*Math.PI+dummyObject.rad
if(time==0){
dummyObject.rad=dst
update()
}else{
if(isDragging)return;
TweenMax.to(dummyObject,time,{rad:dst,delay:delay,onUpdate:update,onComplete:onTwnComp,ease:easing})
isTweening=true
}
function update(){
camera.x=forcus.x+dummyObject.len*Math.cos(dummyObject.rad)
camera.z=forcus.z+dummyObject.len*Math.sin(dummyObject.rad)
}
}
//距離
public function setDistanceTo(len:Number,time:Number=0,delay:Number=0){
if(isTweening)return;
var dst=Number3D.sub(camera.position,forcus.position)
dst.multiplyEq(len/dst.modulo)
dst.plusEq(forcus.position)
if(time==0){
camera.x=dst.x
camera.y=dst.y
camera.z=dst.z
}else{
if(isDragging)return;
TweenMax.to(camera,time,{x:dst.x,y:dst.y,z:dst.z,delay:delay,onComplete:onTwnComp,ease:easing})
isTweening=true
}
}
public function setDistanceBy(len:Number,time:Number=0,delay:Number=0){
if(isTweening)return;
var dst=Number3D.sub(camera.position,forcus.position)
dst.multiplyEq((dst.modulo+len)/dst.modulo)
dst.plusEq(forcus.position)
if(time==0){
camera.x=dst.x
camera.y=dst.y
camera.z=dst.z
}else{
if(isDragging)return;
TweenMax.to(camera,time,{x:dst.x,y:dst.y,z:dst.z,delay:delay,onComplete:onTwnComp,ease:easing})
isTweening=true
}
}
//トゥイーン後処理
private function onTwnComp(){
isTweening=false
onTweenComplete()
}
}
}
ちょっと文字数ヤバそうなので実用例は次に