2007年06月23日
四足アバターを作ってみる ~その4~
ふぅ、すいませんまた間が空きました。
前回までで立ちポーズの作成と、それをセカンドライフにUPしオブジェクトで肉付けするところまで
進みました。
今回は動きのアニメーション(歩く、走る)など必要と思われるものを作成しUPします。
さ、みんなの大好きな(?)QAvimatorからですよ!
今回は動きのアニメーションを作る上で役立つ機能だけサラっと解説します。
いつもの画面↓
準備
・今回は動きのアニメーションなのでフレーム数を多めにします。
・再生/停止ボタンを駆使して動きを確認しましょう!
・途中で再生を止めて編集する場合は、編集すべきフレームかどうか確認しましょう!
(歩きや走るアニメの場合、似ているポーズが多数含まれていますので間違えないようにね)
・例のごとく編集は2フレーム目から!
(1フレーム目を表示すると、地面(?)にあたる部分が赤色にハイライトされますので、地面が赤いときは編集しちゃダメ!って覚えておきましょうね)
作成
・基本的にはパラパラマンガと一緒。
少し動かしては次のフレームに移ってまた少し動かしてと繰り返します。
メニューのコピーとペーストを使えばフレームのポーズを他のフレームに貼り付けていけます。
(滑らかな動きを作るコツは、前のフレームのポーズを貼り付けてから動かしていくことですかね)
また今回のようにオブジェクトで動物の手足を表現したりした場合は
人形のアニメーションだけ見ていても、分かりにくかったりします。
その際に便利なのがこの機能↓
このPropsタブより、QAvimatorのアニメーション用アバターにもオブジェクトをくっつけられます!
あくまで簡易のオブジェクトを添付するだけです、もちろんSLからオブジェクトデータを引っ張ってくることは出来ません。
各項目の説明をしますと
・Prop:object0 こちらのドロップダウンリストから生成したオブジェクトを選択します。
複数製作すればobject0、object1、object2といった感じで
リストに追加されていきます。
+とXのボタン : 最初は何もない状態と思いますので+のボタンを押しましょう、デフォルトで
正方形のオブジェクトが現れるはずです。Xボタンはオブジェクトの消去です。
(先ほどのリストで消去したいオブジェクトを選び、Xで消去といった感じです)
Attach to : 出現させたオブジェクトを体のどの部位に添付するかを選びます。
各部位の名前は、SLと一緒ですね。体に添付させないことも可能です。
(格闘系のアニメーションの場合などで、想定する相手の変わりに同じ大きさの
オブジェクトを置いて作成するなどの使い道がありますね)
Position : オブジェクトの位置です。体に添付しない場合は床から、添付した場合はその部位から
計算されるようです。
Scale : 大きさです。
Rotation : 回転角度です。
今回は馬の製作なので、このように↓添付してアニメーションの製作を行いました。
アニメーションの製作が終わったら前回の立ちポーズと同様SLにUPしましょう。
ここで注意していただきたいのが、UPの作業に入ってもQAvimatorは起動したままに
しておいて下さい。
QavimatorからBVH形式で保存したファイルにはこの簡易オブジェクトのデータは
含まれません。つまり、いったん閉じてしまうとまたオブジェクトを付け直さなければなりません。
初めて動きのアニメーションをUPする際は、きっとすぐに微妙に調節や修正したくなるかと
思いますので、Qavimatorはファイルを開いた状態のままにしておくことをお勧めします。
(アニメーションのUPについてはこちら)
さて、これで全体の8割は完了しました。
ここからSL内での作業に移ります。
基本のアバター(オブジェクトを装着したもの)とアニメーションが揃ったら
次はアニメーションの書き換えスクリプトが必要ですね。
(普段の動きの代わりに、貴方の用意したアニメーションを再生してくれるスクリプトです。)
長くなりますので追記にします!
前回までで立ちポーズの作成と、それをセカンドライフにUPしオブジェクトで肉付けするところまで
進みました。
今回は動きのアニメーション(歩く、走る)など必要と思われるものを作成しUPします。
さ、みんなの大好きな(?)QAvimatorからですよ!
今回は動きのアニメーションを作る上で役立つ機能だけサラっと解説します。
いつもの画面↓
準備
・今回は動きのアニメーションなのでフレーム数を多めにします。
・再生/停止ボタンを駆使して動きを確認しましょう!
・途中で再生を止めて編集する場合は、編集すべきフレームかどうか確認しましょう!
(歩きや走るアニメの場合、似ているポーズが多数含まれていますので間違えないようにね)
・例のごとく編集は2フレーム目から!
(1フレーム目を表示すると、地面(?)にあたる部分が赤色にハイライトされますので、地面が赤いときは編集しちゃダメ!って覚えておきましょうね)
作成
・基本的にはパラパラマンガと一緒。
少し動かしては次のフレームに移ってまた少し動かしてと繰り返します。
メニューのコピーとペーストを使えばフレームのポーズを他のフレームに貼り付けていけます。
(滑らかな動きを作るコツは、前のフレームのポーズを貼り付けてから動かしていくことですかね)
また今回のようにオブジェクトで動物の手足を表現したりした場合は
人形のアニメーションだけ見ていても、分かりにくかったりします。
その際に便利なのがこの機能↓
このPropsタブより、QAvimatorのアニメーション用アバターにもオブジェクトをくっつけられます!
あくまで簡易のオブジェクトを添付するだけです、もちろんSLからオブジェクトデータを引っ張ってくることは出来ません。
各項目の説明をしますと
・Prop:object0 こちらのドロップダウンリストから生成したオブジェクトを選択します。
複数製作すればobject0、object1、object2といった感じで
リストに追加されていきます。
+とXのボタン : 最初は何もない状態と思いますので+のボタンを押しましょう、デフォルトで
正方形のオブジェクトが現れるはずです。Xボタンはオブジェクトの消去です。
(先ほどのリストで消去したいオブジェクトを選び、Xで消去といった感じです)
Attach to : 出現させたオブジェクトを体のどの部位に添付するかを選びます。
各部位の名前は、SLと一緒ですね。体に添付させないことも可能です。
(格闘系のアニメーションの場合などで、想定する相手の変わりに同じ大きさの
オブジェクトを置いて作成するなどの使い道がありますね)
Position : オブジェクトの位置です。体に添付しない場合は床から、添付した場合はその部位から
計算されるようです。
Scale : 大きさです。
Rotation : 回転角度です。
今回は馬の製作なので、このように↓添付してアニメーションの製作を行いました。
アニメーションの製作が終わったら前回の立ちポーズと同様SLにUPしましょう。
ここで注意していただきたいのが、UPの作業に入ってもQAvimatorは起動したままに
しておいて下さい。
QavimatorからBVH形式で保存したファイルにはこの簡易オブジェクトのデータは
含まれません。つまり、いったん閉じてしまうとまたオブジェクトを付け直さなければなりません。
初めて動きのアニメーションをUPする際は、きっとすぐに微妙に調節や修正したくなるかと
思いますので、Qavimatorはファイルを開いた状態のままにしておくことをお勧めします。
(アニメーションのUPについてはこちら)
さて、これで全体の8割は完了しました。
ここからSL内での作業に移ります。
基本のアバター(オブジェクトを装着したもの)とアニメーションが揃ったら
次はアニメーションの書き換えスクリプトが必要ですね。
(普段の動きの代わりに、貴方の用意したアニメーションを再生してくれるスクリプトです。)
長くなりますので追記にします!
セカンドライフ内には仕組み的には殆ど一緒なのですが、この様なスクリプトが仕様違いで数種類存在するようです。
今回はアバターに添付するオブジェクトに仕込むことで、自動的に実行してくれるタイプのものを
紹介します。
今回は「Franimation Overrider FLIP MOD - WAY LESS LAG」というスクリプトを使用してみます。
Tinyアバターなどもこれ同タイプのスクリプトで、動作しています。
(通常のアニメーションを、手足を折りたたんだアニメーションで上書き再生しています)
上記のスクリプトですがフリーウェアのために用意されたGNU General Public Licenseによって
保護されています。
Linuxやオープンソースコミュニティについてご存知の方には身近なものですね。
(当たり前の事ですが、アバターにスクリプトを仕込んで販売する場合などは、スクリプトの金額は勝手に加算しないようにしましょうね。)
準備
・スクリプトを入れ込むためのプリム。(今回は馬のお尻の部分にしました。)
・GNU General Public Licenseスクリプト
・上記のスクリプト用ノートカード(再生するアニメーションをこちらに追記していきます)
手順
・プリムに「スクリプト」、「ノートカード」、「再生するアニメーション」を仕込んでいきます。
以下にスクリプトとノートカードの全文です。<b>(長いですがコメントも読みましょう)</b>
****スクリプト全文**************************************************
// Francis wuz here
// modded by FliperPA to cut down on lag by 1000% without cutting much performance
// also set to listen on alternate channel to stop server parsing everything wearer says in chat
// GREAT WORK FRANCIS! :-)
//
// MOD THE NEXT LINE TO CHOOSE WHICH CHANNEL TO RUN ON
integer LISTEN_CHANNEL = 63;
// Default notecard we read on script_entry
string defaultNoteCard = "ノートカードの名前";
//
// Don't ask me for tech support. I won't give it.
// Copyright (C) 2004 Francis Chung
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 U
// List of all the animation states
list animState = ["Sitting on Ground", "Sitting", "Striding", "Crouching", "CrouchWalking",
"Soft Landing", "Standing Up", "Falling Down", "Hovering Down", "Hovering Up",
"FlyingSlow", "Flying", "Hovering", "Jumping", "PreJumping", "Running",
"Turning Right", "Turning Left", "Walking", "Landing", "Standing" ];
// Index of interesting animations
integer standIndex = 20;
integer sittingIndex = 1;
integer sitgroundIndex = 0;
integer hoverIndex = 12;
integer flyingIndex = 11;
integer flyingslowIndex = 10;
integer hoverupIndex = 9;
integer hoverdownIndex = 8;
integer waterTreadIndex = 25;
integer swimmingIndex = 26;
integer swimupIndex = 27;
integer swimdownIndex = 28;
integer standingupIndex = 6;
// list of animations that have a different value when underwater
list underwaterAnim = [ hoverIndex, flyingIndex, flyingslowIndex, hoverupIndex, hoverdownIndex ];
// corresponding list of animations that we override the overrider with when underwater
list underwaterOverride = [ waterTreadIndex, swimmingIndex, swimmingIndex, swimupIndex, swimdownIndex];
// list of animation states that we need to stop the default animations for
list stopAnimState = [ "Sitting", "Sitting on Ground" ];
// corresponding list of animations to stop when entering that state
list stopAnimName = [ "sit", "sit_ground" ];
// Lines in the notecards where to grab animation names
// This list is indexed the same as list overrides
list lineNums = [ 45, // 0 Sitting on Ground
33, // 1 Sitting
1, // 2 Striding
17, // 3 Crouching
5, // 4 CrouchWalking
39, // 5 Soft Landing
41, // 6 Standing Up
37, // 7 Falling Down
19, // 8 Hovering Down
15, // 9 Hovering Up
43, // 10 FlyingSlow
7, // 11 Flying
31, // 12 Hovering
13, // 13 Jumping
35, // 14 PreJumping
3, // 15 Running
11, // 16 Turning Right
9, // 17 Turning Left
1, // 18 Walking
39, // 19 Landing
21, // 20 Standing 1
23, // 21 Standing 2
25, // 22 Standing 3
27, // 23 Standing 4
29, // 24 Standing 5
47, // 25 Treading Water
49, // 26 Swimming
51, // 27 Swim up
43 // 28 Swim Down
];
// This is an ugly hack, because the standing up animation doesn't work quite right
// (SL is borked, this has been bug reported)
// If you play a pose overtop the standing up animation, your avatar tends to get
// stuck in place.
// This is a list of anims that we'll stop automatically
list autoStop = [ 5, 6, 19 ];
// Amount of time we'll wait before autostopping the animation
float autoStopTime = 1.5;
// List of stands
list standIndexes = [ 20, 21, 22, 23, 24 ];
// How long before flipping stand animations
float standTime = 40.0;
// How fast we should poll for changed anims, changed by FlipperPA to cut lag
// Changed from 0.001 to 0.25
// running at 0.001 doesn't have much benefit; it causes the server to process 1000 times a second
// 0.25 causes server to process only 4 times a second, which in a streaming environment
// should be just fine, with almost no affect noticed by the end user. The cost to the server versus
// benefit to the user is a good chance, IMHO. :-)
float timerEventLength = 0.25;
// Send a message if we encounter a state we've never seen before
integer DEBUG = TRUE;
// GLOBALS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
list stands = [ "", "", "", "", "" ]; // List of stand animations
integer curStandIndex = 0; // Current stand we're on (indexed [0, numStands])
string curStandAnim = ""; // Current Stand animation
integer numStands; // # of stand anims we use (constant: ListLength(stands))
integer curStandAnimIndex = 0; // Current stand we're on (indexed [0, numOverrides] )
list overrides = []; // List of animations we override
list notecardLineKey = []; // notecard reading keys
integer notecardLinesRead; // number of notecard lines read
integer numOverrides; // # of overrides (a constant - llGetListLength(lineNums))
string lastAnim = ""; // last Animation we ever played
integer lastAnimIndex = 0; // index of the last animation we ever played
string lastAnimState = ""; // last thing llGetAnimation() returned
integer animOverrideOn = TRUE; // Is the animation override on?
integer gotPermission = FALSE; // Do we have animation permissions?
integer listenHandler0; // Listen handlers
// CODE
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
list listReplace ( list _source, list _newEntry, integer _index ) {
return llListInsertList( llDeleteSubList(_source,_index,_index), _newEntry, _index );
}
startNewAnimation( string _anim, integer _animIndex, string _state ) {
if ( _anim != lastAnim ) {
if ( _anim != "" ) { // Time to play a new animation
llStartAnimation( _anim );
if ( _state != lastAnimState && llListFindList(stopAnimName, [_state]) != -1 ) {
// Stop the default sit/sit ground animation
llStopAnimation( llList2String(stopAnimName, llListFindList(stopAnimName, [_state])) );
}
else if ( llListFindList( autoStop, [_animIndex] ) != -1 ) {
// This is an ugly hack, because the standing up animation doesn't work quite right
// (SL is borked, this has been bug reported)
// If you play a pose overtop the standing up animation, your avatar tends to get
// stuck in place.
if ( lastAnim != "" ) {
llStopAnimation( lastAnim );
lastAnim = "";
}
llSleep( autoStopTime );
llStopAnimation( _anim );
}
}
if ( lastAnim != "" )
llStopAnimation( lastAnim );
lastAnim = _anim;
}
lastAnimIndex = _animIndex;
lastAnimState = _state;
}
// Load all the animation names from a notecard
loadNoteCard( string _notecard ) {
integer i;
if ( llGetInventoryKey(_notecard) == NULL_KEY ) {
llSay( 0, "Notecard '" + _notecard + "' does not exist." );
return;
}
llInstantMessage( llGetOwner(), "Loading notecard '" + _notecard + "'..." );
// Start reading the data
notecardLinesRead = 0;
notecardLineKey = [];
for ( i=0; i<numOverrides; i++="" )="">
notecardLineKey += [ llGetNotecardLine( _notecard, llList2Integer(lineNums,i) ) ];
}
// Figure out what animation we should be playing right now
animOverride() {
string curAnimState = llGetAnimation(llGetOwner());
integer curAnimIndex = llListFindList( animState, [curAnimState] );
integer underwaterAnimIndex = llListFindList( underwaterAnim, [curAnimIndex] );
vector curPos = llGetPos();
if ( curAnimState == lastAnimState ) {
// Do nothing
// This conditional not absolutely necessary (In fact it's better if it's not here)
// But it's good for increasing performance.
} else if ( curAnimIndex == -1 ) {
if ( DEBUG )
llInstantMessage( llGetOwner(), "Unknown animation state '" + curAnimState + "'" );
}
else if ( curAnimIndex == standIndex ) {
startNewAnimation( curStandAnim, curStandAnimIndex, curAnimState );
}
else {
if ( underwaterAnimIndex != -1 && llWater(ZERO_VECTOR) > curPos.z )
curAnimIndex = llList2Integer( underwaterOverride, underwaterAnimIndex );
startNewAnimation( llList2String(overrides,curAnimIndex), curAnimIndex, curAnimState );
}
}
// Initialize listeners, and reset some status variables
initialize() {
if ( animOverrideOn )
llSetTimerEvent( timerEventLength );
else
llSetTimerEvent( 0 );
lastAnim = "";
lastAnimIndex = -1;
lastAnimState = "";
gotPermission = FALSE;
if ( listenHandler0 )
llListenRemove( listenHandler0 );
listenHandler0 = llListen( LISTEN_CHANNEL, "", llGetOwner(), "" );
}
// STATE
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
default {
state_entry() {
integer i;
if ( llGetAttached() )
llRequestPermissions(llGetOwner(),PERMISSION_TRIGGER_ANIMATION|PERMISSION_TAKE_CONTROLS);
// Initialize!
numStands = llGetListLength( stands );
numOverrides = llGetListLength(lineNums);
curStandAnimIndex = llList2Integer(standIndexes,curStandIndex);
// populate override list with blanks
for ( i=0; i<numOverrides; i++="" )="" {="">
overrides += [ "" ];
}
initialize();
loadNoteCard( defaultNoteCard );
llResetTime();
}
run_time_permissions(integer parm) {
if( parm == (PERMISSION_TRIGGER_ANIMATION|PERMISSION_TAKE_CONTROLS) ) {
llTakeControls( CONTROL_DOWN|CONTROL_UP|CONTROL_FWD|CONTROL_BACK|CONTROL_LEFT|CONTROL_RIGHT
|CONTROL_ROT_LEFT|CONTROL_ROT_RIGHT, TRUE, TRUE);
gotPermission = TRUE;
}
}
attach( key k ) {
if ( k != NULL_KEY )
llRequestPermissions(llGetOwner(),PERMISSION_TRIGGER_ANIMATION|PERMISSION_TAKE_CONTROLS);
}
listen(integer _channel, string _name, key _id,string _message) {
if( _message == "ao on" ) {
llSetTimerEvent( timerEventLength );
animOverrideOn = TRUE;
if ( gotPermission )
animOverride();
llInstantMessage( llGetOwner(), "Franimation override on." );
}
else if ( _message == "ao off" ) {
llSetTimerEvent( 0 );
animOverrideOn = FALSE;
startNewAnimation( "", -1, lastAnimState );
llInstantMessage( llGetOwner(), "Franimation override off." );
}
else if ( _message == "ao hide" ) {
llSetLinkAlpha( LINK_SET, 0, ALL_SIDES );
llInstantMessage( llGetOwner(), "Franimation override set invisible." );
}
else if ( _message == "ao show" ) {
llSetLinkAlpha( LINK_SET, 1, ALL_SIDES );
llInstantMessage( llGetOwner(), "Franimation override set visible." );
}
else if ( _message == "ao reset" ) {
llResetScript();
}
}
dataserver(key _query_id, string _data) {
integer index = llListFindList( notecardLineKey, [_query_id] );
if ( _data != EOF && index != -1 ) { // not at the end of the notecard and not random crap
if ( index == curStandAnimIndex ) // Pull in the current stand animation
curStandAnim = _data;
if ( index == lastAnimIndex ) // Whoops, we're replacing the currently playing anim
startNewAnimation( _data, lastAnimIndex, lastAnimState ); // Better play the new one :)
// Store the name of the new animation
overrides = listReplace( overrides, [_data], index );
// See if we're done loading the notecard. Users like status messages.
if ( ++notecardLinesRead == numOverrides )
llInstantMessage( llGetOwner(), "Finished reading notecard. (" +
(string) llGetFreeMemory() + " bytes free)" );
}
}
on_rez( integer _code ) {
initialize();
}
touch_start(integer _total_number) {
}
control( key _id, integer _level, integer _edge ) {
if ( animOverrideOn && gotPermission )
animOverride();
}
timer() {
if ( animOverrideOn && gotPermission ) {
animOverride();
if ( llGetTime() > standTime ) {
curStandIndex = (curStandIndex+1) % numStands;
curStandAnimIndex = llList2Integer(standIndexes,curStandIndex);
curStandAnim = llList2String(overrides, curStandAnimIndex);
if ( lastAnimState == "Standing" )
startNewAnimation( curStandAnim, curStandAnimIndex, lastAnimState );
llResetTime();
}
}
}
}
****ノートカード全文**************************************************
-=Walking=-
歩きアニメーションの名前
-=Running=-
走りアニメーションの名前
-=Crouchwalk=-
しゃがみ歩きアニメーションの名前
-=Flying=-
飛行アニメーションの名前
-=Normal Turn Left=-
左回転アニメーションの名前
-=Normal Turn Right=-
右回転アニメーションの名前
-=Jumping=-
ジャンプアニメーションの名前
-=Fly Up=-
飛翔時アニメーションの名前
-=Crouching=-
しゃがみアニメーションの名前
-=Fly Down=-
下降アニメーションの名前
-=Stand1=-
立ちアニメーションの名前
-=Stand2=-
以下も名前に相応のアニメーション(手抜き?)
-=Stand3=-
-=Stand4=-
-=Stand5=-
-=Hover=-
-=Sitting=-
-=PreJump=-
-=Falling=-
-=Soft Landing=-
-=Hard Landing=-
-=Flying Slow=-
-=Sitting on Ground=-
-=Treading Water (hover underwater)=-
-=Swimming=-
-=Swim Up=-
-=Swim Down=-
******ここまで*******************************
プリムに仕込み終わったら動作チェックしてみましょう!
(仕込んだプリムを装備して動き回ってみる)
ノートカードは全ての項目を埋める必要はありません、アニメーションをオリジナルで
用意しない場合は空白の行のままで結構です。
standのアニメーションが複数あるのは、ランダムか定期的かわかりませんが複数のアニメーション
を再生する機能があるためです。
(チェックはしてないですが、行を詰めたりとかはしないほうがいいかも)
動作に問題がなければ、本格的にプリムの造形も進めましょう!
あの不細工な白馬ですがこんな感じにしてみました↓
ここまでくれば、あとは気の済むまでいじり倒せば完了です!
次回は補足的にskinの製作について解説しますね~
今回はアバターに添付するオブジェクトに仕込むことで、自動的に実行してくれるタイプのものを
紹介します。
今回は「Franimation Overrider FLIP MOD - WAY LESS LAG」というスクリプトを使用してみます。
Tinyアバターなどもこれ同タイプのスクリプトで、動作しています。
(通常のアニメーションを、手足を折りたたんだアニメーションで上書き再生しています)
上記のスクリプトですがフリーウェアのために用意されたGNU General Public Licenseによって
保護されています。
Linuxやオープンソースコミュニティについてご存知の方には身近なものですね。
(当たり前の事ですが、アバターにスクリプトを仕込んで販売する場合などは、スクリプトの金額は勝手に加算しないようにしましょうね。)
準備
・スクリプトを入れ込むためのプリム。(今回は馬のお尻の部分にしました。)
・GNU General Public Licenseスクリプト
・上記のスクリプト用ノートカード(再生するアニメーションをこちらに追記していきます)
手順
・プリムに「スクリプト」、「ノートカード」、「再生するアニメーション」を仕込んでいきます。
以下にスクリプトとノートカードの全文です。<b>(長いですがコメントも読みましょう)</b>
****スクリプト全文**************************************************
// Francis wuz here
// modded by FliperPA to cut down on lag by 1000% without cutting much performance
// also set to listen on alternate channel to stop server parsing everything wearer says in chat
// GREAT WORK FRANCIS! :-)
//
// MOD THE NEXT LINE TO CHOOSE WHICH CHANNEL TO RUN ON
integer LISTEN_CHANNEL = 63;
// Default notecard we read on script_entry
string defaultNoteCard = "ノートカードの名前";
//
// Don't ask me for tech support. I won't give it.
// Copyright (C) 2004 Francis Chung
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 U
// List of all the animation states
list animState = ["Sitting on Ground", "Sitting", "Striding", "Crouching", "CrouchWalking",
"Soft Landing", "Standing Up", "Falling Down", "Hovering Down", "Hovering Up",
"FlyingSlow", "Flying", "Hovering", "Jumping", "PreJumping", "Running",
"Turning Right", "Turning Left", "Walking", "Landing", "Standing" ];
// Index of interesting animations
integer standIndex = 20;
integer sittingIndex = 1;
integer sitgroundIndex = 0;
integer hoverIndex = 12;
integer flyingIndex = 11;
integer flyingslowIndex = 10;
integer hoverupIndex = 9;
integer hoverdownIndex = 8;
integer waterTreadIndex = 25;
integer swimmingIndex = 26;
integer swimupIndex = 27;
integer swimdownIndex = 28;
integer standingupIndex = 6;
// list of animations that have a different value when underwater
list underwaterAnim = [ hoverIndex, flyingIndex, flyingslowIndex, hoverupIndex, hoverdownIndex ];
// corresponding list of animations that we override the overrider with when underwater
list underwaterOverride = [ waterTreadIndex, swimmingIndex, swimmingIndex, swimupIndex, swimdownIndex];
// list of animation states that we need to stop the default animations for
list stopAnimState = [ "Sitting", "Sitting on Ground" ];
// corresponding list of animations to stop when entering that state
list stopAnimName = [ "sit", "sit_ground" ];
// Lines in the notecards where to grab animation names
// This list is indexed the same as list overrides
list lineNums = [ 45, // 0 Sitting on Ground
33, // 1 Sitting
1, // 2 Striding
17, // 3 Crouching
5, // 4 CrouchWalking
39, // 5 Soft Landing
41, // 6 Standing Up
37, // 7 Falling Down
19, // 8 Hovering Down
15, // 9 Hovering Up
43, // 10 FlyingSlow
7, // 11 Flying
31, // 12 Hovering
13, // 13 Jumping
35, // 14 PreJumping
3, // 15 Running
11, // 16 Turning Right
9, // 17 Turning Left
1, // 18 Walking
39, // 19 Landing
21, // 20 Standing 1
23, // 21 Standing 2
25, // 22 Standing 3
27, // 23 Standing 4
29, // 24 Standing 5
47, // 25 Treading Water
49, // 26 Swimming
51, // 27 Swim up
43 // 28 Swim Down
];
// This is an ugly hack, because the standing up animation doesn't work quite right
// (SL is borked, this has been bug reported)
// If you play a pose overtop the standing up animation, your avatar tends to get
// stuck in place.
// This is a list of anims that we'll stop automatically
list autoStop = [ 5, 6, 19 ];
// Amount of time we'll wait before autostopping the animation
float autoStopTime = 1.5;
// List of stands
list standIndexes = [ 20, 21, 22, 23, 24 ];
// How long before flipping stand animations
float standTime = 40.0;
// How fast we should poll for changed anims, changed by FlipperPA to cut lag
// Changed from 0.001 to 0.25
// running at 0.001 doesn't have much benefit; it causes the server to process 1000 times a second
// 0.25 causes server to process only 4 times a second, which in a streaming environment
// should be just fine, with almost no affect noticed by the end user. The cost to the server versus
// benefit to the user is a good chance, IMHO. :-)
float timerEventLength = 0.25;
// Send a message if we encounter a state we've never seen before
integer DEBUG = TRUE;
// GLOBALS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
list stands = [ "", "", "", "", "" ]; // List of stand animations
integer curStandIndex = 0; // Current stand we're on (indexed [0, numStands])
string curStandAnim = ""; // Current Stand animation
integer numStands; // # of stand anims we use (constant: ListLength(stands))
integer curStandAnimIndex = 0; // Current stand we're on (indexed [0, numOverrides] )
list overrides = []; // List of animations we override
list notecardLineKey = []; // notecard reading keys
integer notecardLinesRead; // number of notecard lines read
integer numOverrides; // # of overrides (a constant - llGetListLength(lineNums))
string lastAnim = ""; // last Animation we ever played
integer lastAnimIndex = 0; // index of the last animation we ever played
string lastAnimState = ""; // last thing llGetAnimation() returned
integer animOverrideOn = TRUE; // Is the animation override on?
integer gotPermission = FALSE; // Do we have animation permissions?
integer listenHandler0; // Listen handlers
// CODE
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
list listReplace ( list _source, list _newEntry, integer _index ) {
return llListInsertList( llDeleteSubList(_source,_index,_index), _newEntry, _index );
}
startNewAnimation( string _anim, integer _animIndex, string _state ) {
if ( _anim != lastAnim ) {
if ( _anim != "" ) { // Time to play a new animation
llStartAnimation( _anim );
if ( _state != lastAnimState && llListFindList(stopAnimName, [_state]) != -1 ) {
// Stop the default sit/sit ground animation
llStopAnimation( llList2String(stopAnimName, llListFindList(stopAnimName, [_state])) );
}
else if ( llListFindList( autoStop, [_animIndex] ) != -1 ) {
// This is an ugly hack, because the standing up animation doesn't work quite right
// (SL is borked, this has been bug reported)
// If you play a pose overtop the standing up animation, your avatar tends to get
// stuck in place.
if ( lastAnim != "" ) {
llStopAnimation( lastAnim );
lastAnim = "";
}
llSleep( autoStopTime );
llStopAnimation( _anim );
}
}
if ( lastAnim != "" )
llStopAnimation( lastAnim );
lastAnim = _anim;
}
lastAnimIndex = _animIndex;
lastAnimState = _state;
}
// Load all the animation names from a notecard
loadNoteCard( string _notecard ) {
integer i;
if ( llGetInventoryKey(_notecard) == NULL_KEY ) {
llSay( 0, "Notecard '" + _notecard + "' does not exist." );
return;
}
llInstantMessage( llGetOwner(), "Loading notecard '" + _notecard + "'..." );
// Start reading the data
notecardLinesRead = 0;
notecardLineKey = [];
for ( i=0; i<numOverrides; i++="" )="">
notecardLineKey += [ llGetNotecardLine( _notecard, llList2Integer(lineNums,i) ) ];
}
// Figure out what animation we should be playing right now
animOverride() {
string curAnimState = llGetAnimation(llGetOwner());
integer curAnimIndex = llListFindList( animState, [curAnimState] );
integer underwaterAnimIndex = llListFindList( underwaterAnim, [curAnimIndex] );
vector curPos = llGetPos();
if ( curAnimState == lastAnimState ) {
// Do nothing
// This conditional not absolutely necessary (In fact it's better if it's not here)
// But it's good for increasing performance.
} else if ( curAnimIndex == -1 ) {
if ( DEBUG )
llInstantMessage( llGetOwner(), "Unknown animation state '" + curAnimState + "'" );
}
else if ( curAnimIndex == standIndex ) {
startNewAnimation( curStandAnim, curStandAnimIndex, curAnimState );
}
else {
if ( underwaterAnimIndex != -1 && llWater(ZERO_VECTOR) > curPos.z )
curAnimIndex = llList2Integer( underwaterOverride, underwaterAnimIndex );
startNewAnimation( llList2String(overrides,curAnimIndex), curAnimIndex, curAnimState );
}
}
// Initialize listeners, and reset some status variables
initialize() {
if ( animOverrideOn )
llSetTimerEvent( timerEventLength );
else
llSetTimerEvent( 0 );
lastAnim = "";
lastAnimIndex = -1;
lastAnimState = "";
gotPermission = FALSE;
if ( listenHandler0 )
llListenRemove( listenHandler0 );
listenHandler0 = llListen( LISTEN_CHANNEL, "", llGetOwner(), "" );
}
// STATE
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
default {
state_entry() {
integer i;
if ( llGetAttached() )
llRequestPermissions(llGetOwner(),PERMISSION_TRIGGER_ANIMATION|PERMISSION_TAKE_CONTROLS);
// Initialize!
numStands = llGetListLength( stands );
numOverrides = llGetListLength(lineNums);
curStandAnimIndex = llList2Integer(standIndexes,curStandIndex);
// populate override list with blanks
for ( i=0; i<numOverrides; i++="" )="" {="">
overrides += [ "" ];
}
initialize();
loadNoteCard( defaultNoteCard );
llResetTime();
}
run_time_permissions(integer parm) {
if( parm == (PERMISSION_TRIGGER_ANIMATION|PERMISSION_TAKE_CONTROLS) ) {
llTakeControls( CONTROL_DOWN|CONTROL_UP|CONTROL_FWD|CONTROL_BACK|CONTROL_LEFT|CONTROL_RIGHT
|CONTROL_ROT_LEFT|CONTROL_ROT_RIGHT, TRUE, TRUE);
gotPermission = TRUE;
}
}
attach( key k ) {
if ( k != NULL_KEY )
llRequestPermissions(llGetOwner(),PERMISSION_TRIGGER_ANIMATION|PERMISSION_TAKE_CONTROLS);
}
listen(integer _channel, string _name, key _id,string _message) {
if( _message == "ao on" ) {
llSetTimerEvent( timerEventLength );
animOverrideOn = TRUE;
if ( gotPermission )
animOverride();
llInstantMessage( llGetOwner(), "Franimation override on." );
}
else if ( _message == "ao off" ) {
llSetTimerEvent( 0 );
animOverrideOn = FALSE;
startNewAnimation( "", -1, lastAnimState );
llInstantMessage( llGetOwner(), "Franimation override off." );
}
else if ( _message == "ao hide" ) {
llSetLinkAlpha( LINK_SET, 0, ALL_SIDES );
llInstantMessage( llGetOwner(), "Franimation override set invisible." );
}
else if ( _message == "ao show" ) {
llSetLinkAlpha( LINK_SET, 1, ALL_SIDES );
llInstantMessage( llGetOwner(), "Franimation override set visible." );
}
else if ( _message == "ao reset" ) {
llResetScript();
}
}
dataserver(key _query_id, string _data) {
integer index = llListFindList( notecardLineKey, [_query_id] );
if ( _data != EOF && index != -1 ) { // not at the end of the notecard and not random crap
if ( index == curStandAnimIndex ) // Pull in the current stand animation
curStandAnim = _data;
if ( index == lastAnimIndex ) // Whoops, we're replacing the currently playing anim
startNewAnimation( _data, lastAnimIndex, lastAnimState ); // Better play the new one :)
// Store the name of the new animation
overrides = listReplace( overrides, [_data], index );
// See if we're done loading the notecard. Users like status messages.
if ( ++notecardLinesRead == numOverrides )
llInstantMessage( llGetOwner(), "Finished reading notecard. (" +
(string) llGetFreeMemory() + " bytes free)" );
}
}
on_rez( integer _code ) {
initialize();
}
touch_start(integer _total_number) {
}
control( key _id, integer _level, integer _edge ) {
if ( animOverrideOn && gotPermission )
animOverride();
}
timer() {
if ( animOverrideOn && gotPermission ) {
animOverride();
if ( llGetTime() > standTime ) {
curStandIndex = (curStandIndex+1) % numStands;
curStandAnimIndex = llList2Integer(standIndexes,curStandIndex);
curStandAnim = llList2String(overrides, curStandAnimIndex);
if ( lastAnimState == "Standing" )
startNewAnimation( curStandAnim, curStandAnimIndex, lastAnimState );
llResetTime();
}
}
}
}
****ノートカード全文**************************************************
-=Walking=-
歩きアニメーションの名前
-=Running=-
走りアニメーションの名前
-=Crouchwalk=-
しゃがみ歩きアニメーションの名前
-=Flying=-
飛行アニメーションの名前
-=Normal Turn Left=-
左回転アニメーションの名前
-=Normal Turn Right=-
右回転アニメーションの名前
-=Jumping=-
ジャンプアニメーションの名前
-=Fly Up=-
飛翔時アニメーションの名前
-=Crouching=-
しゃがみアニメーションの名前
-=Fly Down=-
下降アニメーションの名前
-=Stand1=-
立ちアニメーションの名前
-=Stand2=-
以下も名前に相応のアニメーション(手抜き?)
-=Stand3=-
-=Stand4=-
-=Stand5=-
-=Hover=-
-=Sitting=-
-=PreJump=-
-=Falling=-
-=Soft Landing=-
-=Hard Landing=-
-=Flying Slow=-
-=Sitting on Ground=-
-=Treading Water (hover underwater)=-
-=Swimming=-
-=Swim Up=-
-=Swim Down=-
******ここまで*******************************
プリムに仕込み終わったら動作チェックしてみましょう!
(仕込んだプリムを装備して動き回ってみる)
ノートカードは全ての項目を埋める必要はありません、アニメーションをオリジナルで
用意しない場合は空白の行のままで結構です。
standのアニメーションが複数あるのは、ランダムか定期的かわかりませんが複数のアニメーション
を再生する機能があるためです。
(チェックはしてないですが、行を詰めたりとかはしないほうがいいかも)
動作に問題がなければ、本格的にプリムの造形も進めましょう!
あの不細工な白馬ですがこんな感じにしてみました↓
ここまでくれば、あとは気の済むまでいじり倒せば完了です!
次回は補足的にskinの製作について解説しますね~
Posted by masaru at 00:51│Comments(0)
│製作