2013年12月14日 星期六

我的玄鳳小寶貝 アキ aki

aki今天好乖 讓我摸摸頭 按摩了好久好久~











然後乖乖完,太晚了 該睡覺了~抓回籠子裡 aki就開始吃飯了~
拿了甜竹要放進 猛攻擊我的手指啊 這個熊孩子...
明明飛羽還沒長出來 硬是要飛 摔的連尾羽都掉光了 這樣比較短髪俏麗嗎...
幾個月前 不知聊到什麼 老哥說出了 太陽鳥這個詞 唷 好帥氣的名字啊..
然後 我一回台北開始找怎麼入手太陽鳥~本來想跟鳥友買的
可是沒養過好像都不太敢賣給我XD
記得跟鳥店買來的第一天......鳥很怕 窩在角落 一直站在飲食杯上...
後來 店家打電話過來 說我抓錯隻了XD 抓到母的 哈哈
我就又回去換了一隻 哈哈 跟第一隻情況一樣啊 怕生的勒
我在買之前名字早就取好了~就叫AKI(取自日劇海女的女主角名字,現在想說下一支就叫YUI)
然後我抓錯的那隻猛叫AKI,拿到正宗AKI的時候,我一度考慮換個名字>"<
吶吶 一開始尾羽還很漂亮的~快點長回來吧~快點學會飛吧~

自從我第一次硬把他放到肩膀上後,他後來就很愛爬上我肩膀 趕都趕不下去啊
而且從以前到現在都很不愛讓人碰他,就要咬咬咬
現在最常發生 要把他從肩膀抓下來 就要被他咬咬咬
今天在腳踏車上做了個站棍 打算找天騎車帶他出去 (不知行不行的通 掉下去就GG了)
趁他現在還不會飛 不用穿飛行衣,常帶他出門買菜 買東西
說到飛行衣,他還是很不習慣啊 沒關係 慢慢來 我想你飛羽也還沒長好...
改天本仙幫你做一件熊孩子樣式的!!

2013年8月7日 星期三

COCOS2D-X LUA eclipse debug 中斷

先下載ldt Lua Development Tools
http://www.eclipse.org/koneki/ldt/
安裝完之後,就可以進行單純的lua腳本debug跟中斷了!

那麼~我們接下來要進行的是,
因為我是使用c++來寫cocos2d-x,build出來的是exe檔
裡面有一些自已加進去的lib,如luasocket、cjson、luaproxy,跟自定義的一些功能
在lua腳本裡也會使用到這些東西,如果我直接進行debug的話,
一定會彈出找不到這些自定義功能的錯誤。
所以,debug的時候,開啟的程式,一定會是build出來的那一支exe檔~

也就是說 在eclipse按下debug,接著打開exe檔,會連結到eclipse 進入debug的模式~
這裡的連結是用socket實現

即然用到socket,也就是說lua腳本裡要能使用luasocket這個lib。

1.方法大致上是,eclipse進入debug,等待目標程式連結。
2.目前程式使用debugger.lua 搭配luasocket的lib 進行與eclipse的連結
3.連結上,就可以進入debug模式嚕~

※在debug configurations 裡的 lua attach to application 增加一個connect
project選你要的project
ide key 這個之後會填寫在debugger.lua的init參數中

※ debugger.lua是由ide自已產生的

※假設我們lua腳本入口是 "main.lua",
那就在開頭加上
require "ltn12"
require "socket"
require "mime"
require('debugger')("localhost","10000",'luaidekey')
其中len12、socket、mime,都是debugger.lua會用到的module
所以要先呼叫進來
接著debugger這支其實是呼叫裡面的
local function init(host, port, idekey, transport, executionplatform, workingdirectory)
參數的解說很詳細了~



2013年7月15日 星期一

BSD SOCKET on lua筆記

目的:在win32 android iphone上使用socket進行通訊
ide:vs2012 express、sublime text2

安裝:
win32:
拿掉bsd socket包裡的unix檔案,
serial.c
unix.c
unix.h
usocket.c
usocket.h
這時後編譯會出現一堆 無法解析的外部符號
1.在連結加入pthreadVCE2.lib
2.在wsocket.h 
#include <winsock2.h>後面加上一行
#pragma comment(lib,"ws2_32.lib")
編譯通過

unix(android):
拿掉wsocket.c wsocket.h
就可以通過了


lua:
這時後在lua上就能使用local socket = require("socket.core")
以及local mime = require("mime.core")
但是我們不需要直接做這2個操作
中間還有一層interface,
http://files.luaforge.net/releases/luasocket/luasocket
下載luascoket,我是載2.0.2
將下載回來的資料依照
http://w3.impa.br/~diego/software/luasocket/installation.html
所示放入lua資料夾內
可以看到socket.lua這支有使用到 require("socket.core")
mine.lua也有使用到 require("mime.core")

在程式一開始
require "socket"
require "mine"
之後 就可以直接使用
local socket = require("socket")
進行socket操作了!

以下先參考一篇文章
該文章實作lua版的server跟client
http://dhq.me/luasocket-network-lua-module

接著,我們來實作nodejs的server,以及client 的lua

待續...

2013年6月2日 星期日

FMS-FLASH MEDIA SERVER 視訊網站架設教學(3)

即然不小心標題多打了視訊2個字
現在要改也來不及惹QQ
但視訊網站的模式有很多種
我想 這樣好了
我們來做這個~
點擊房間列表裡的使用者 然後可以選 觀看對方視訊
而LAYOUT也要改一下
如果是觀看對方視訊的同時,聊天介面就會切成上下2半
上面是視訊
視訊再分成2半,左邊是對方,右邊是自已
然後要看對方視訊時,會有邀請的要求給對方
對方同意後 才會打開 視訊的介面出來
這時候第三方要邀請已經在視訊中的人,都會回傳 對方已在視訊聊天中
嗯嗯 就這樣~那就開始ㄅ

通常server code會比較容易看懂,也比較容易了解整個的流程
延展教學(2)的範例,新增了幾個主要的method
client.invite = function(id)//發起邀請
client.sendInviteResponse = function(id,isAccept)//接收方回應是否接受邀請
application.onPublish = function(client, myStream)//發佈視訊
client.exitPrivate = function()//通知要離開private視訊
exitPrivate(user)//離開private視訊

這裡比較重要的地方是,要發佈一個視訊,需要有一個stream name
可以自已定一個字串,但不可以重覆。
假如a發佈一個video_a的串流,那麼 其他使用者只要去聽這個串流名稱video_a就可以看到a所發佈的視訊

client的程式裡有一個特別的用法,你可以在限定參數的event function內,帶入各種想要的參數
Alert.show(userName+"邀請你進入視訊聊天,是否接受?", "hasInvite", Alert.OK|Alert.NO,null,addInviteArguments(onHasInvite,id));
private function addInviteArguments(method:Function, id:String):Function
{
return function(event:Event):void {method.apply(null, [event].concat(id));}
}
private function onHasInvite(e:CloseEvent,id:String):void{
mineNc.call("sendInviteResponse",null,id,Boolean(e.detail==Alert.OK));
}
常常會遇到這種onEvent的參數,不能多帶參數的情況,這時候使用這個方式,可以任意帶多個參數進去;就不用另外做一個變數來記著了。

其它部份就直接看code吧~

server code:

var roomUser = new Array();
var ERROR_REPEAT_LOGIN=0;

var USER_STATUS_NORMAL=0;
var USER_STATUS_PRIVATE=1;

var INVITE_RESULT_PRIVATE=0;
var INVITE_RESULT_SUCCESS=1;
var INVITE_RESULT_OFF_LINE=2;
var INVITE_RESULT_REJECT=3;
application.onAppStart = function (){ 
 
}
// Called when a client connects
application.onConnect = function(client,userName) {
 var _user=getUserByUserName(userName);
 if(_user)
 {
  application.rejectConnection(client,ERROR_REPEAT_LOGIN);
  return;
 }
 
 var user=new Object();
 user.userName=userName;
 user.id=client.id;
 user.client=client;
 user.status=USER_STATUS_NORMAL;
 user.streamId="";
 user.targetUserId=null;
 roomLogin(user);
 
 client.chatToAll = function(msg) {
  var user=getUserByClient(client);
  application.broadcastMsg("chatReceive",user.userName,msg);
 }
 //發起邀請
 client.invite = function(id) {
  var user=getUserByClient(client);
  var targetUser=getUserById(id);
  if(targetUser)
  {
   if(targetUser.status==USER_STATUS_PRIVATE)
    client.call("onInvite",null,INVITE_RESULT_PRIVATE);//回應 對方已在視訊中
   else
   {
    client.call("onInvite",null,INVITE_RESULT_SUCCESS);//回應 等待對方接受
    targetUser.client.call("hasInvite",null,user.id,user.userName);//通知 對方 有人邀請你
   }
  }
  else
   client.call("onInvite",null,INVITE_RESULT_OFF_LINE);//回應 對方已不在線上
 }
 //接收方回應是否接受邀請
 client.sendInviteResponse = function(id,isAccept) {
  var user=getUserByClient(client);
  var targetUser=getUserById(id);
  if(isAccept)//如果同意 就把雙方各建立一個stream的id,並讓雙方得知自已跟對方的stream id
  {
   var r=Math.floor(Math.random()*1000);
   user.streamId=r+user.id;
   targetUser.streamId=r+targetUser.id;
   //事實上這裡可以做個更好的加密 比如說拿雙方的id再加上一個變數來md5一下
   user.targetUserId=targetUser.id;
   targetUser.targetUserId=user.id;
   //互相記住對方的id,若斷線的時後可以通知對方
   client.call("intoPrivate",null,user.streamId,targetUser.streamId);
   targetUser.client.call("intoPrivate",null,targetUser.streamId,user.streamId);
   //進入雙人視訊模式 參數1是自已的stream id,參數2是對方的stream id
   user.status=USER_STATUS_PRIVATE;
   targetUser.status=USER_STATUS_PRIVATE;
  }
  else
   targetUser.client.call("onInvite",null,INVITE_RESULT_REJECT);
 }
 client.exitPrivate = function() {
  var user=getUserByClient(client);
  exitPrivate(user);
 }
}

// Called when the client publishes
application.onPublish = function(client, myStream) {
  nc = new NetConnection();
  ns = new NetStream(nc);
  ns.setBufferTime(2);
  ns.attach(myStream);
  ns.publish( myStream.name, "live" );
}

application.onUnpublish = function( client, myStream ) {
var user=getUserByClient(client);
application.broadcastMsg("systemMessage",user.userName+":"+myStream.name);
    trace(myStream.name + " is unpublishing"  );
}

// Called when a client disconnects
application.onDisconnect = function(client) {
 var user=getUserByClient(client);
 exitPrivate(user);
 removeUser(user);
}

function exitPrivate(user)
{
 user.status=USER_STATUS_NORMAL;
 user.targetUserId=null;
 var targetUser=getUserById(user.targetUserId);
 if(targetUser)
 {
  targetUser.status=USER_STATUS_NORMAL;
  targetUser.targetUserId=null;
  targetUser.client.call("onExitPrivate",null);
 }
}

function roomLogin(user)
{
 application.acceptConnection(user.client);
 application.broadcastMsg("systemMessage", user.userName+" in room");
 roomUser.push(user);
 application.broadcastMsg("userList",roomUser);
 user.client.call("loginSuccess",null,user);
}

function removeUser(user){
 var ind=roomUser.indexOf(user);
 if(ind!=-1)
 {
  roomUser.splice(ind,1);
  application.broadcastMsg("userList",roomUser);
 }

}

function getUserByUserName(userName){
 for(var i=0;i<roomUser.length;i++)
  {
   if(roomUser[i].userName==userName)
   {
    return roomUser[i];
   }
  }
  return null;
}

function getUserByClient(client){
 for(var i=0;i<roomUser.length;i++)
  {
   if(roomUser[i].client==client)
   {
    return roomUser[i];
   }
  }
  return null;
}

function getUserById(id){
 for(var i=0;i<roomUser.length;i++)
  {
   if(roomUser[i].id==id)
   {
    return roomUser[i];
   }
  }
  return null;
}




client code:

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
      xmlns:s="library://ns.adobe.com/flex/spark" applicationComplete="application1_applicationCompleteHandler(event)"
      xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
 <fx:Script>
  <![CDATA[
   import flash.net.NetStream;
   import flash.utils.setTimeout;
   
   import mx.collections.ArrayCollection;
   import mx.controls.Alert;
   import mx.events.CloseEvent;
   import mx.events.FlexEvent;
   
   private var server_ip:String="rtmp://localhost/chat";
   private var mineNc:NetConnection;
   private var mineNs:NetStream;
   private var mineVideo:Video;
   private var mineCamera:Camera;
   private var mineMic:Microphone;
   private const cameraW:Number=400;
   private const cameraH:Number=300;
   
   private var targetVideo:Video;
   private var targetNs:NetStream;
   
   private var STATE_MODE_NOTPRIVATE:String="notPrivate";
   private var STATE_MODE_ISPRIVATE:String="isPrivate";
   [Bindable]
   private var user:Object;
   [Bindable]
   private var isLoign:Boolean=false;
   [Bindable]
   private var userArr:ArrayCollection=new ArrayCollection();
   
   //跟server對應的狀態變數
   private const INVITE_RESULT_PRIVATE:int=0;
   private const INVITE_RESULT_SUCCESS:int=1;
   private const INVITE_RESULT_OFF_LINE:int=2;
   private const INVITE_RESULT_REJECT:int=3;
   
   protected function application1_applicationCompleteHandler(event:FlexEvent):void
   {
    chat_input.addEventListener(KeyboardEvent.KEY_UP,onChatKeyUp);
   }
   private function login():void
   {
    var name:String=name_txt.text;
    if(name!="")
    {
     mineNc = new NetConnection();
     mineNc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
     mineNc.connect(server_ip,name);
     login_group.visible=false;
     
     var ncClient:Object = new Object(); 
     mineNc.client = ncClient; 
     ncClient.systemMessage = systemMessage;
     ncClient.userList = userList;
     ncClient.chatReceive = chatReceive;
     ncClient.onInvite = onInvite;
     ncClient.hasInvite = hasInvite;
     ncClient.intoPrivate = intoPrivate;
     ncClient.loginSuccess = loginSuccess;
     ncClient.onExitPrivate = onExitPrivate;
    }
    else
     Alert.show("請輸入登入別名", "ERROR", Alert.OK,null);
   }
   
   
   private function onExitPrivate():void
   {
    this.currentState=STATE_MODE_NOTPRIVATE;
    mineNs.attachCamera(null);
   }
   private function loginSuccess(user:Object):void
   {
    this.user=user;
   }
   
   private function intoPrivate(streamId:String,targetStreamId:String):void
   {
    systemMessage("my straemId:"+streamId+",target StreamId:"+targetStreamId);
    this.currentState=STATE_MODE_ISPRIVATE;
    publishLiveStream(streamId);
    this.addRemoteVideo(targetStreamId);
   }
   
   private function publishLiveStream(streamName:String):void {
    mineNs = new NetStream(mineNc);
    mineNs.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
    
    mineCamera = Camera.getCamera();
    mineMic = Microphone.getMicrophone();
    if (mineCamera != null){
     mineCamera.setQuality(0,100);
     mineCamera.setMode(cameraW,cameraH,28);
     mineCamera.setKeyFrameInterval(60);
     mineCamera.addEventListener(ActivityEvent.ACTIVITY, activityHandler);
     mineVideo = new Video();
     mineVideo.attachCamera(mineCamera);
     mineVideo.width=cameraW;
     mineVideo.height=cameraH;
     mineVideo.smoothing = true;
     mineNs.attachCamera(mineCamera);
     mineVideoDisplay.addChild(mineVideo);
    }
    
    if (mineMic != null) {
     mineMic.addEventListener(ActivityEvent.ACTIVITY, activityHandler);
     mineNs.attachAudio(mineMic);
    }
    if (mineCamera != null || mineMic != null){
     mineNs.publish(streamName, "live");
    } else {
     trace("Please check your camera and microphone");
    }
   }
   
   private function activityHandler(event:ActivityEvent):void {
    trace("activityHandler: " + event);
    trace("activating: " + event.activating);
   }
   private function addRemoteVideo(streamName:String,id:String="",name:String=""):void{
    targetNs=new NetStream(mineNc);
    targetNs.play(streamName);
    targetVideo = new Video();
    targetVideo.width=cameraW;
    targetVideo.height=cameraH;
    targetVideo.attachNetStream(targetNs);
    targetVideo.smoothing = true;
    tagetVideoDisplay.addChild(targetVideo);
    tagetVideoDisplay.visible=true;
   }
   
   private function hasInvite(id:String,userName:String):void
   {
    Alert.show(userName+"邀請你進入視訊聊天,是否接受?", "hasInvite", Alert.OK|Alert.NO,null,addInviteArguments(onHasInvite,id));
    //這裡有一個特別的用法,學會它,你可以在限定參數的event function內,帶入各種想要的參數
   }
   private function addInviteArguments(method:Function, id:String):Function 
   {
    return function(event:Event):void {method.apply(null, [event].concat(id));}
   }
   private function onHasInvite(e:CloseEvent,id:String):void{
    mineNc.call("sendInviteResponse",null,id,Boolean(e.detail==Alert.OK));
   }
   
   private function onInvite(resultCode:int):void
   {
    switch(resultCode)
    {
     case INVITE_RESULT_PRIVATE:
      Alert.show("對方已在視訊聊天中", "onInvite", Alert.OK,null);
      break;
     case INVITE_RESULT_SUCCESS:
      Alert.show("等待對方回應中...", "onInvite", Alert.OK,null);
      break;
     case INVITE_RESULT_OFF_LINE:
      Alert.show("對方已不在線上", "onInvite", Alert.OK,null);
      break;
     case INVITE_RESULT_REJECT:
      Alert.show("對方捥拒你的邀請", "onInvite", Alert.OK,null);
      break;
     default:
      Alert.show("未知錯誤", "onInvite", Alert.OK,null);
      break;
    }
   }
   private function chatReceive(senderName:String,msg:String):void
   {
    msg=senderName+":"+msg+String.fromCharCode(13);
    content_txt.text+=msg;
   }
   
   private function netStatusHandler(event:NetStatusEvent):void
   {
    trace(event.info.code);
    switch (event.info.code)
    {
     case "NetConnection.Connect.Success":
      isLoign=true;
      break;
     case "NetConnection.Connect.Closed":
      isLoign=false;
      Alert.show("Closed", "ERROR", Alert.OK,null);
      break;
     case "NetConnection.Connect.Rejected":
      isLoign=false;
      loginFail(event.info.application); 
      break;
    }
   }
   private function loginFail(type:int):void{
    switch(type)
    {
     case 0:
      Alert.show("重覆登入", "ERROR", Alert.OK,null);
      break;
     default:
      Alert.show("Rejected", "ERROR", Alert.OK,null);
      break;
    }
   }
   private function onChatKeyUp(evt:KeyboardEvent):void
   {   
    if (evt.keyCode == Keyboard.ENTER)
    {
     mineNc.call("chatToAll", null,chat_input.text);
     chat_input.text="";
    }
   }
   
   private function systemMessage(msg:String):void
   { 
    msg+=String.fromCharCode(13);
    content_txt.text+=msg;
   }
   
   private function userList(list:Object):void
   {
    userArr=new ArrayCollection;
    for(var i:int=0;i<list.length;i++)
    {
     userArr.addItem(list[i]);
    }
   }
   
   private function invite():void
   {
    if(room_list.selectedIndex<0)return;
    var user:Object=userArr.getItemAt(room_list.selectedIndex);
    if(user.id==this.user.id)
    {
     Alert.show("不能邀請自已", "ERROR", Alert.OK,null);
     return;
    }
    if(this.currentState==STATE_MODE_ISPRIVATE)
    {
     Alert.show("你已在private中", "ERROR", Alert.OK,null);
     return;
    }
    if(user)
    {
     mineNc.call("invite", null,user.id);
    }
   }
   
   private function exitPrivate():void
   {
    mineNc.call("exitPrivate", null);
    onExitPrivate();
   }
  ]]>
 </fx:Script>
 <s:states>
  <s:State name="notPrivate" />
  <s:State name="isPrivate" />
 </s:states>
 <s:HGroup width="100%" height="100%">
  <s:VGroup width="20%" height="100%">
   <s:List id="room_list" width="100%" height="95%" dataProvider="{userArr}" itemRenderer="userItem" verticalScrollPolicy="auto" horizontalScrollPolicy="off" contentBackgroundAlpha="0.0" borderVisible="false"/>
   <s:HGroup width="100%" height="5%">
    <s:Button label="邀請視訊" click="invite()" enabled="{isLoign}" />
   </s:HGroup>
  </s:VGroup>
  <s:VGroup  width="80%" height="100%">
   <s:HGroup width="100%" height="100%" includeIn="isPrivate">
    <mx:VideoDisplay id="tagetVideoDisplay" width="{cameraW}" height="{cameraH}"/>
    <mx:VideoDisplay id="mineVideoDisplay" width="{cameraW}" height="{cameraH}"/>
    <s:Button label="exit" click="exitPrivate()"/>
   </s:HGroup>
   <s:TextArea editable="false" id="content_txt" width="100%" height="90%"/>
   <s:TextInput id="chat_input"  width="100%" height="10%"/>
  </s:VGroup>
 </s:HGroup>
 <s:HGroup id="login_group" right="10" top="10">
  <s:TextInput width="100" id="name_txt"/>
  <s:Button label="login" click="login()"/>
 </s:HGroup>
</s:Application>




下一次,來增加在server上呼叫php api的功能,順便補上一對一密語的功能。~

2013年5月29日 星期三

Adobe AIR: use ExtensionContext call api on android

為了這個功能我搞了快2天,難的地方在於下command...
很多教學都是把command的格式打上,可是對於不常用的我來說 根本不知道要怎麼填〒▽〒
總而言之 後來我會了 之前都是用flash build生成apk,現在不僅會用adt生apk跟ane,還學會ant耶~
即然air有了這個功能,當然是要把所有遊戲中可能會用到的 admob、facebook、iap都實作一次~
這裡先來講~單純的flash ExtensionContext 類別的實用~而native就用toast驗證一下是否成功就好惹~


需要做的東西:
1.一支jar,實作android的功能,我把命名為AndroidExtension.jar
2.一支swc,實作AndroidExtension.jar跟air的interface,我把命名為AirExtensionLib.swc
3.一支swf,就是用air做出來的介面,我把它命名為AirExtensionUsage.swf


實做的流程:
1. AndroidExtension.jar ,先把要擴充的功能實作出來,並且試試看效果(在這裡是把toast做出來,雖然沒2行= =)
2.AirExtensionLib.swc實作
3.AirExtensionUsage.swf實作
4.生成ane檔(air的資源檔),把AndroidExtension.jar跟AirExtensionLib.swc包成ane。
   AndroidExtension.jar裡如果使用到其它jar,需要先把這些jar統統包成一包,包完也叫AndroidExtension.jar就好了
5.再把ane檔跟swf以及app.xml(air 裡的設定檔)統統包成apk 
6.測試->完成

批次檔 LOOP FOR 對所有資料夾做動作

echo echo start > temp.bat
for /f %%i in ('dir/AD/B') do (
echo COPY data.txt %%i >> temp.bat
)
call temp.bat
pause
del temp.bat

※由於for迴圈中 只能有一行描述句
故在一開始先建立一個temp.bat
並寫入命令 echo start
該迴圈訪問所有的資料夾
並在每個迴圈中將命令句 COPY data.txt %%i 加入 temp.bat中
 ※>是建立檔案並寫入(a),>>是寫入檔案(w)
迴圈結束後,
再執行(call) temp.bat
最後 使用del 將 temp.bat刪除

FMS-FLASH MEDIA SERVER 視訊網站架設教學(2)

這篇主要建立一個簡易的聊天室
登入後,會更新房間人員的清單
發送聊天訊息
使用同一人名登入 會顯示登入失敗

SERVER CODE-main.asc

var roomUser = new Array();
var ERROR_REPEAT_LOGIN=0;
application.onAppStart = function (){ 
 
}
// Called when a client connects
application.onConnect = function(client,userName) {
 var _user=getUserByUserName(userName);
 if(_user)
 {
  application.rejectConnection(client,ERROR_REPEAT_LOGIN);
  return;
 }
 
 var user=new Object();
 user.userName=userName;
 user.id=client.id;
 user.client=client;
 roomLogin(user);
 
 client.chatToAll = function(msg) {
  var user=getUserByClient(client);
  application.broadcastMsg("chatReceive",user.userName,msg);
 }
}

// Called when a client disconnects
application.onDisconnect = function(client) {
 var user=getUserByClient(client);
 removeUser(user);
}

function roomLogin(user)
{
 application.acceptConnection(user.client);
 application.broadcastMsg("systemMessage", user.userName+" in room");
 roomUser.push(user);
 application.broadcastMsg("userList",roomUser);
}

function removeUser(user){
 var ind=roomUser.indexOf(user);
 if(ind!=-1)
 {
  roomUser.splice(ind,1);
  application.broadcastMsg("userList",roomUser);
 }

}

function getUserByUserName(userName){
 for(var i=0;i<roomUser.length;i++)
  {
   if(roomUser[i].userName==userName)
   {
    return roomUser[i];
   }
  }
  return null;
}

function getUserByClient(client){
 for(var i=0;i<roomUser.length;i++)
  {
   if(roomUser[i].client==client)
   {
    return roomUser[i];
   }
  }
  return null;
}





CLIENT CODE-chatRoom.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
      xmlns:s="library://ns.adobe.com/flex/spark" applicationComplete="application1_applicationCompleteHandler(event)"
      xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600">
 <fx:Script>
  <![CDATA[
   import flash.net.NetStream;
   import flash.utils.setTimeout;
   
   import mx.collections.ArrayCollection;
   import mx.controls.Alert;
   import mx.events.FlexEvent;
   
   private var server_ip:String="rtmp://localhost/chat";
   private var mineNc:NetConnection;
   private var mineNs:NetStream;
   [Bindable]
   private var userArr:ArrayCollection=new ArrayCollection();
   protected function application1_applicationCompleteHandler(event:FlexEvent):void
   {
    chat_input.addEventListener(KeyboardEvent.KEY_UP,onChatKeyUp);
   }
   private function login():void
   {
    var name:String=name_txt.text;
    if(name!="")
    {
     mineNc = new NetConnection();
     mineNc.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
     mineNc.connect(server_ip,name);
     login_group.visible=false;
     
     var ncClient:Object = new Object(); 
     mineNc.client = ncClient; 
     ncClient.systemMessage = systemMessage;
     ncClient.userList = userList;
     ncClient.chatReceive = chatReceive;
    }
    else
     Alert.show("請輸入登入別名", "ERROR", Alert.OK,null);
   }
   private function netStatusHandler(event:NetStatusEvent):void
   {
    trace(event.info.code);
    switch (event.info.code)
    {
     case "NetConnection.Connect.Success":
      break;
     case "NetConnection.Connect.Closed":
      Alert.show("Closed", "ERROR", Alert.OK,null);
      break;
     case "NetConnection.Connect.Rejected":
      loginFail(event.info.application); 
      break;
    }
   }
   private function loginFail(type:int):void{
    switch(type)
    {
     case 0:
      Alert.show("重覆登入", "ERROR", Alert.OK,null);
      break;
     default:
      Alert.show("Rejected", "ERROR", Alert.OK,null);
      break;
    }
   }
   private function onChatKeyUp(evt:KeyboardEvent):void
   {   
    if (evt.keyCode == Keyboard.ENTER)
    {
     mineNc.call("chatToAll", null,chat_input.text);
     chat_input.text="";
    }
   }
   
   private function systemMessage(msg:String):void
   { 
    msg+=String.fromCharCode(13);
    content_txt.text+=msg;
   }
   
   private function userList(list:Object):void
   {
    userArr=new ArrayCollection;
    for(var i:int=0;i<list.length;i++)
    {
     userArr.addItem(list[i]);
    }
   }
   
   private function chatReceive(senderName:String,msg:String):void
   {
    msg=senderName+":"+msg+String.fromCharCode(13);
    content_txt.text+=msg;
   }
  ]]>
 </fx:Script>
 <s:HGroup width="100%" height="100%">
  <s:List id="room_list" width="20%" height="100%" dataProvider="{userArr}" itemRenderer="userItem" verticalScrollPolicy="auto" horizontalScrollPolicy="off" contentBackgroundAlpha="0.0" borderVisible="false"/>
  <s:VGroup  width="80%" height="100%">
   <s:TextArea editable="false" id="content_txt" width="100%" height="90%"/>
   <s:TextInput id="chat_input"  width="100%" height="10%"/>
  </s:VGroup>
 </s:HGroup>
 <s:HGroup id="login_group" right="10" top="10">
  <s:TextInput width="100" id="name_txt"/>
  <s:Button label="login" click="login()"/>
 </s:HGroup>
</s:Application>


CLIENT CODE-userItem.mxml

<?xml version="1.0" encoding="utf-8"?>
<s:ItemRenderer xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx" width="90" height="21" creationComplete="itemrenderer1_creationCompleteHandler(event)">
 <fx:Script>
  <![CDATA[
   import mx.events.FlexEvent;
   
   protected function itemrenderer1_creationCompleteHandler(event:FlexEvent):void
   {
    // TODO Auto-generated method stub
    
   }
   
  ]]>
 </fx:Script>
 <s:Label text="{data.userName}" verticalCenter="0"/>
</s:ItemRenderer>


這篇要注意的幾點
server呼叫單一個client的method 是使用client.call
server廣播所有client的method是application.broadcastMsg
以上2個方法 都需要在application.accept
Connection(client);
允許該client加入之後 ,才可使用。

application.rejectConnection(client,ERROR_REPEAT_LOGIN);
若不同意使用者登入 可以使用rejectConnection
rejectConnection是可以帶參數回去的

其餘的應該都很簡單 就不多做講解了~

FMS-FLASH MEDIA SERVER 視訊網站架設教學(1-基本認知)

flash media server 是一套在server上運行as語言的工具
主要是用來當as3的連線server,可以用來做多人視訊,多人遊戲,live播放等需要server的功能
以下簡稱fms

fms的寫作方式 較不同於as3,主要是不能給型態
var i:int=3;
function foo():void
這些都會發生error
且能使用的function、api也是自成一套
http://help.adobe.com/en_US/FlashMediaServer/3.5_SS_ASD/WS5b3ccc516d4fbf351e63e3d11a11aff5ba-7f8b.html
這裡是線上的api 可供參考
至於ide,也無法使用flash builder 或 flash developer
目前找到能用的ide是SEPY ActionScript Editor,用起來還過的去- -

fms也無法使用namespace
舉反 public private等...都是無法使用的

trace的功能,會印在該server的log裡

fms上所有server都放在applications/底下
要增加一個新的server就多開一個資料夾
起始檔是main.asc (後綴是.asc)

import的方式需改成 load
例如: load("config.asc");
load的使用方式基本上跟import一樣
共用的package會放在scriptlib/底下
※例如我們新建一個json.asc
所有的server都有可能會用到,我們就把這個asc檔存放在scriptlib底下
使用時 load("json.asc");

在main.asc裡,application這個變數是內部變數,就是指目前的server
其自帶的屬性跟function請參考這裡
http://help.adobe.com/en_US/FlashMediaServer/3.5_SS_ASD/WS5b3ccc516d4fbf351e63e3d11a11afc95e-7edf.html
所有server的狀態都可以由這些方法得知跟實現

當有使用者登入,會自動參生一個client的變數,代表該位使用者
跟server一樣,client也有自已的class集成,參考以下
http://help.adobe.com/en_US/FlashMediaServer/3.5_SS_ASD/WS5b3ccc516d4fbf351e63e3d11a11afc95e-7ec3.html

ok 以上是基本認知
接下來我們會以做個簡易聊天室來了解fms的寫作方式~

2013年5月14日 星期二

烏鴨女

美國土 先捏出大體

修一下

細節處理







頭毛先裁斷,翻完模之後再用補土接上




只翻身體 矽膠翻模

還不錯


腳就用水晶土翻了 不然太浪費時間跟金錢了

水晶土真的要好好翻 不然之後打磨可能會很痛苦













done


2013年4月21日 星期日

cocos2d-x jsoncpp load file

很久很久沒碰c++了
近日在寫cocos2d x,因為遊戲中要定義怪物的資料,所以必需載入怪物的資料設定表
因為之前都是用as3寫遊戲,所以希望能做個像在as3的manager
以下就是實作的結果,因為不太會c++,所以要是有用的不好 或是有更好的方法,請不吝告知。

構想:載入本地端的json檔,因為可能會載入很多支json,所以要先存在一個固定的地方,
使用時,再用一個function 取出想要的資料。

環境佈置:
vs 2012 express
cocos2d-x 2.1.2(在這裡跟cocos2d哪個版本沒有影響~)
jsoncpp...呃 版本我也不知道是多少,我是從http://cocos2d.cocoachina.com/bbs/forum.php?mod=viewthread&tid=266&highlight=jsoncpp 這篇文章中的附件載下來使用的
因為這好像是說解決了中文編碼的問題,我就直接拿來用了。可能之後再補上一篇
更詳細的文章。

※所以,總而言之,你需要先確定你能正常的使用jsoncpp。
怎麼確認正不正常。
你先引入
#include "json.h"
再程式碼中加一句 reader
Json::Reader reader;
試著跑跑看,假如沒出現error,那應該就是配置完成了:)

正式開始:

1.建立一個json檔,我的範例
[{"id":100,"name":"Knight","info":"騎士","healthy":110,"attack":7},{"id":101,"name":"mage","info":"法師","healthy":80,"attack":5}]

//不知道為什麼上面的key都會變成大寫,原文件都是小寫- -
把它存成avatar.json

2.解析並儲存json
先看第一個function

void JsonManager::setJsonFromFile(const char* fileName,const char* name,const char* key)
{
Json::Reader reader;
ifstream file(getFullPath(fileName));//載入json檔
CCAssert(file.is_open(), "file is open fail!");
Json::Value root;
if  (!reader.parse(file, root, false )) {
CCAssert(false, "Json::Reader Parse error!");
}
saveList(root,name,key);//寫入
};
setJsonFromFile 這個function主要是載入json檔,並將之儲存起來。
以我寫遊戲的經驗來說,json檔都是一開始就載入進來的,直到要使用的時候,才另外提取裡面的資料。
當然 會有例外,例如解析socket傳來的json資料,就是要當下解析並立即使用的,如果是這樣 ,那就不用saveList了,而是直接return root;

至於saveList

map<string,Json::Value> maps;
map<string,string> key_maps;


void JsonManager::saveList(Json::Value value,const char* name,const char* key)
{
key_maps[name]=key;
maps[name]=value;
}
只是將解析完的資料儲存在map裡。
我也是第一次使用map,用得不是很暢快,我為了連key一起存,所以用了2個map..一個儲json的資料,一個儲key。

這樣子我們就可以在遊戲一進入的時候,使用
#define AVATAR "avatar"
JsonManager::setJsonFromFile("avatar.json",AVATAR,"id");
把json的資料先儲存在記憶體裡了。
參數1是 json檔的檔名
參數2是 這個json的索引值 (因為我們可能會載入一大堆json檔,要提取其中一份使用的key就是靠這個)
參數3是 提取該json資料中的key,在這裡,我的key都會是int的型態。(就是說id指的值會是int)

3.取值
我們已經載進了一份avatar.json 並把化存在 maps["avatar"]裡。
現在要提取這份資料中的其中一筆,KEY是id
使用以下的function getData

Json::Value JsonManager::getData(const char* name,int id)
{
map<string,string>::iterator key_it;
map<string,Json::Value>::iterator it;
key_it=key_maps.find(name);
string key;
if(key_it!=key_maps.end())//先找出在key_maps中有沒有這個key
{
key=key_it->second;//有的話就把key值記下來,這裡也就是之前傳入的"id"
}
else
return NULL;
it=maps.find(name);
if(it!=maps.end())//再找找看maps裡有沒有"avatar"的JSON資料
{
Json::Value arrayObj=it->second;//有找到就繼續比對 其中的id,是不是跟傳入的id是一樣的。
for (unsigned int i = 0; i < arrayObj.size(); i++)
        {   
int s = arrayObj[i][key].asInt();
if(s==id)
return (Json::Value)arrayObj[i];//如果找到一樣的id 就傳回這個資料行
        } 
return NULL;
}
return NULL;
}


在程式碼中的使用如下

 Json::Value myjson =   JsonManager::getData(AVATAR,100);
string s = myjson["name"].asString();
CCLog("%s::::",s.c_str());//印出 knight:::::
string info = myjson["info"].asString();
CCLog("%s::::",info.c_str());//印出 騎士:::::
int atk = myjson["attack"].asInt();
CCLog("%d::::",atk);//印出 7::::::

okay 基本上想要的功能都實現了~

以下附上完整的JsonManager class的原始碼

----JsonManager .h----


#ifndef __JSONMANAGER_H__
#define __JSONMANAGER_H__
#define AVATAR "avatar"
#include "jsoncpp\json.h"
#include "Basic.h"

USING_NS_CC;
class JsonManager{
public:
static void setJsonFromFile(const char* fileName,const char* name,const char* key);
static const char* getFullPath(const char* path);
static void saveList(Json::Value value,const char* name,const char* key);
static Json::Value getData(const char* name,int id);
};
#endif



-----JsonManager .cpp-------


#include "JsonManager.h"
#include <iostream>
#include <fstream>
#include <string>
#include <map>
using namespace std;
map<string,Json::Value> maps;
map<string,string> key_maps;

void JsonManager::setJsonFromFile(const char* fileName,const char* name,const char* key)
{
Json::Reader reader;
ifstream file(getFullPath(fileName));
CCAssert(file.is_open(), "file is open fail!");
Json::Value root;
if  (!reader.parse(file, root, false )) {
CCAssert(false, "Json::Reader Parse error!");
}
saveList(root,name,key);
};
Json::Value JsonManager::getData(const char* name,int id)
{
map<string,string>::iterator key_it;
map<string,Json::Value>::iterator it;
key_it=key_maps.find(name);
string key;
if(key_it!=key_maps.end())
{
key=key_it->second;
}
else
return NULL;
it=maps.find(name);
if(it!=maps.end())
{
Json::Value arrayObj=it->second;
for (unsigned int i = 0; i < arrayObj.size(); i++)
        {   
int s = arrayObj[i][key].asInt();
if(s==id)
return (Json::Value)arrayObj[i];
        } 
return NULL;
}
return NULL;
}
void JsonManager::saveList(Json::Value value,const char* name,const char* key)
{
key_maps[name]=key;
maps[name]=value;
}
const char* JsonManager::getFullPath(const char* path)
{
return cocos2d::CCFileUtils::sharedFileUtils()->fullPathFromRelativeFile(path,path);
};


※Json:Value 可以將他想像成一個object格式,裡面可能是string int或array