ブラウザ上でゲーム開発を行える「Code.9leap.net」,β版サービスが始まる
これにあたり、「code.9leapカスタム・チュートリアルチャレンジ!」というコンテストが開催されました。
っということで、私もチャレンジしてみようと思います!
私がenchant.jsで最初に悩んだところは画面遷移でした。
ということで、そのあたりのチュートリアルを作ってみました 。
ゲームの基本「タイトル画面 → プレイ画面 → エンド画面」の流れを簡単に説明しています。
かっしぃの日々のメモ帳です。 気づいたことや思ったこと、プログラミングのコツ、 最近気になることなどをメモしてます。 Twitter http://twitter.com/kassy708
HRESULT NuiInitialize(
DWORD dwFlags
)
KINECT_DEPTH_WIDTHを#defineで事前に定義して、ここで条件分岐しています。
KinectSDKはBetaの時はDepth画像の解像度を320x240しか設定できなかったのですがenchant.PhyPolygonSprite(width, height, vertexs, type, density, friction, restitution, awake)
var width = 20; var height = 50; var vertexs = new Array( new b2Vec2(-width / 2, -height / 2), new b2Vec2(width / 2, -height / 2), new b2Vec2(width / 2, height / 2), new b2Vec2(-width / 2, height / 2)); var phyPolygonSprite = new PhyPolygonSprite(width, height, vertexs, DYNAMIC_SPRITE, 1.0, 0.1, 0.2, true); game.rootScene.addChild(phyPolygonSprite); // シーンに追加
var vertexCount = 5;
var radius = 20;
var vertexs = new Array();
for (var i = 0; i < vertexCount; i++) {
vertexs[i] = new b2Vec2(radius * Math.cos(2 * Math.PI / vertexCount * i), radius * Math.sin(2 * Math.PI / vertexCount * i));
}
var phyPolygonSprite = new PhyPolygonSprite(radius * 2, radius * 2, vertexs, DYNAMIC_SPRITE, 1.0, 0.1, 0.2, true);
game.rootScene.addChild(phyPolygonSprite); // シーンに追加
//軸
var axis = new PhyCircleSprite(8, STATIC_SPRITE);
axis.position = { x: 160, y: 160 };
game.rootScene.addChild(axis); // シーンに追加
//ボール生成
var ball = new PhyCircleSprite(8, DYNAMIC_SPRITE);
ball.position = { x: 100, y: 250 };
game.rootScene.addChild(ball); // シーンに追加
//距離ジョイント
var joint = new PhyDistanceJoint(axis, ball);
//軸
var axis = new PhyCircleSprite(8, STATIC_SPRITE);
axis.position = { x: 160, y: 160 };
game.rootScene.addChild(axis); // シーンに追加
//ボール生成
var ball = new PhyCircleSprite(8, DYNAMIC_SPRITE);
ball.position = { x: 100, y: 250 };
game.rootScene.addChild(ball); // シーンに追加
//距離ジョイント
var joint = new PhyRevoluteJoint(axis, ball);
joint.enableMotor = true;
joint.maxMotorTorque = 100;
joint.motorSpeed = 90;
var box = new PhyBoxSprite(16, 8, DYNAMIC_SPRITE, 1.0, 0.5, 0.2, true);
box.position = { x: game.width * 2 / 3, y: game.height / 2 };
var prismaticAxis = new b2Vec2(1.0, 0); //x軸にスライドを設定(右が正の値)
//スライドジョイント
var prismaticJoint = new PhyPrismaticJoint(box, prismaticAxis);
//スライドオブジェクトにモーター機能を持たせる場合
//prismaticJoint.enableMotor = true; //モータの有効化
//prismaticJoint.maxMotorForce = 100.0; //モータの最大力を設定
//prismaticJoint.motorSpeed = 50; //モータの速度を設定
var ball1 = new PhyCircleSprite(8, DYNAMIC_SPRITE);
ball1.position = { x: 80, y: 160 };
var ball2 = new PhyCircleSprite(8, DYNAMIC_SPRITE);
ball2.position = { x: 240, y: 160 };
//滑車ジョイント
var pulleyJoint = new PhyPulleyJoint(ball1, ball2, new b2Vec2(80, 100), new b2Vec2(240, 100), 1);
GLCapture glCapture;
void init(){
//glCaptureの設定
glCapture.setWriteFile("output.avi");
}
void display(){
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
//~描画するプログラム~
glFlush();
glutSwapBuffers();
//動画ファイルに書き込み
glCapture.write();
}
と、こんな感じです。context.FindExistingNode(XN_NODE_TYPE_USER, userGenerator); xn::SkeletonCapability skeleton = userGenerator.GetSkeletonCap(); // ユーザー認識のコールバックを登録 // キャリブレーションのコールバックを登録 XnCallbackHandle userCallbacks, calibrationCallbacks; userGenerator.RegisterUserCallbacks(&::UserDetected, 0, 0, userCallbacks); skeleton.RegisterCalibrationCallbacks( 0, &::CalibrationEnd, 0, calibrationCallbacks ); skeleton.SetSkeletonProfile(XN_SKEL_PROFILE_ALL);
// ユーザー検出
void XN_CALLBACK_TYPE UserDetected( xn::UserGenerator& generator, XnUserID nId, void* pCookie )
{
printf("ユーザID%d 検出 %d人目\n",nId,generator.GetNumberOfUsers());
generator.GetSkeletonCap().RequestCalibration(nId, TRUE);
}
// キャリブレーションの終了
void XN_CALLBACK_TYPE CalibrationEnd(xn::SkeletonCapability& capability, XnUserID nId, XnBool bSuccess, void* pCookie)
{
if ( bSuccess ) {
printf("ユーザID%d キャリブレーション成功\n",nId);
capability.StartTracking(nId);
}
else {
printf("ユーザID%d キャリブレーション失敗\n",nId);
}
}
XnUserID aUsers[15];
XnUInt16 nUsers = 15;
userGenerator.GetUsers(aUsers, nUsers);
for (int i = 0; i < nUsers; ++i){
if (userGenerator.GetSkeletonCap().IsTracking(aUsers[i])){
XnSkeletonJointPosition joint;
//aUsers[i]の頭部位置をjointに格納
userGenerator.GetSkeletonCap().GetSkeletonJointPosition(aUsers[i], XN_SKEL_HEAD, joint);
}
}
#include <GL/glut.h>
#include <opencv2/opencv.hpp>
#ifdef _DEBUG
//Debugモードの場合
#pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_core220d.lib") // opencv_core
#pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_imgproc220d.lib") // opencv_imgproc
#pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_highgui220d.lib") // opencv_highgui
#else
//Releaseモードの場合
#pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_core220.lib") // opencv_core
#pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_imgproc220.lib") // opencv_imgproc
#pragma comment(lib,"C:\\OpenCV2.2\\lib\\opencv_highgui220.lib") // opencv_highgui
#endif
#include <XnCppWrapper.h>
#pragma comment(lib,"C:/Program files/OpenNI/Lib/openNI.lib")
#define SAMPLE_XML_PATH "C:/Program Files/OpenNI/Data/SamplesConfig.xml"
using namespace cv;
using namespace xn;
//openNIのための宣言・定義
//マクロ定義
#define KINECT_IMAGE_WIDTH 640
#define KINECT_IMAGE_HEGIHT 480
#define KINECT_DEPTH_WIDTH 640
#define KINECT_DEPTH_HEGIHT 480
DepthGenerator depthGenerator;// depth context
ImageGenerator imageGenerator;//image context
UserGenerator userGenerator;
DepthMetaData depthMD;
ImageMetaData imageMD;
Context context;
//トラッキングの際のコールバック
void XN_CALLBACK_TYPE UserDetected( xn::UserGenerator& generator, XnUserID nId, void* pCookie );
void XN_CALLBACK_TYPE CalibrationEnd(xn::SkeletonCapability& capability, XnUserID nId, XnBool bSuccess, void* pCookie);
Mat image(480,640,CV_8UC3);
Mat depth(480,640,CV_16UC1);
//ポイントクラウドの座標
Mat pointCloud_XYZ(480,640,CV_32FC3,cv::Scalar::all(0));
void retrievePointCloudMap(Mat &depth,Mat &pointCloud_XYZ); //3次元ポイントクラウドのための座標変換
void drawPointCloud(Mat &rgbImage,Mat &pointCloud_XYZ); //ポイントクラウド描画
void drawUserJoint(); //ユーザの関節を描画
void DrawLimb(XnUserID player, XnSkeletonJoint eJoint1, XnSkeletonJoint eJoint2);
void DrawJoint(XnUserID player, XnSkeletonJoint eJoint1);
//openGLのための宣言・定義
//---変数宣言---
int FormWidth = 640;
int FormHeight = 480;
int mButton;
float twist, elevation, azimuth;
float cameraDistance = 0,cameraX = 0,cameraY = 0;
int xBegin, yBegin;
//---マクロ定義---
#define glFovy 45 //視角度
#define glZNear 1.0 //near面の距離
#define glZFar 150.0 //far面の距離
void polarview(); //視点変更
//描画
void display(){
// clear screen and depth buffer
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
// Reset the coordinate system before modifying
glLoadIdentity();
glEnable(GL_DEPTH_TEST); //「Zバッファ」を有効
gluLookAt(0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0, 0.0); //視点の向き設定
//wait and error processing
context.WaitAnyUpdateAll();
imageGenerator.GetMetaData(imageMD);
depthGenerator.GetMetaData(depthMD);
depthGenerator.GetAlternativeViewPointCap().SetViewPoint(imageGenerator);//ズレを補正
memcpy(image.data,imageMD.Data(),image.step * image.rows); //イメージデータを格納
memcpy(depth.data,depthMD.Data(),depth.step * depth.rows); //深度データを格納
//3次元ポイントクラウドのための座標変換
retrievePointCloudMap(depth,pointCloud_XYZ);
//視点の変更
polarview();
//ポイントクラウド
drawPointCloud(image,pointCloud_XYZ);
//関節の描画
drawUserJoint();
//convert color space RGB2BGR
cvtColor(image,image,CV_RGB2BGR);
imshow("image",image);
imshow("depth",depth);
glFlush();
glutSwapBuffers();
}
//初期化
void init(){
context.InitFromXmlFile(SAMPLE_XML_PATH);
context.FindExistingNode(XN_NODE_TYPE_DEPTH, depthGenerator);
context.FindExistingNode(XN_NODE_TYPE_IMAGE, imageGenerator);
context.FindExistingNode(XN_NODE_TYPE_USER, userGenerator);
if (!userGenerator.IsCapabilitySupported(XN_CAPABILITY_SKELETON)) { // スケルトン・トラッキングをサポートしているか確認
printf("ユーザー検出をサポートしてません\n");
}
// キャリブレーションにポーズが必要
xn::SkeletonCapability skeleton = userGenerator.GetSkeletonCap();
if ( skeleton.NeedPoseForCalibration() ) {
printf("このOpenNIのバージョンはポーズ無しには対応していません\n");
}
// ユーザー認識のコールバックを登録
// キャリブレーションのコールバックを登録
XnCallbackHandle userCallbacks, calibrationCallbacks;
userGenerator.RegisterUserCallbacks(&::UserDetected, 0, 0, userCallbacks);
skeleton.RegisterCalibrationCallbacks( 0, &::CalibrationEnd, 0, calibrationCallbacks );
skeleton.SetSkeletonProfile(XN_SKEL_PROFILE_ALL);
}
// アイドル時のコールバック
void idle(){
//再描画要求
glutPostRedisplay();
}
//ウィンドウのサイズ変更
void reshape (int width, int height){
FormWidth = width;
FormHeight = height;
glViewport (0, 0, (GLsizei)width, (GLsizei)height);
glMatrixMode (GL_PROJECTION);
glLoadIdentity ();
//射影変換行列の指定
gluPerspective (glFovy, (GLfloat)width / (GLfloat)height,glZNear,glZFar);
glMatrixMode (GL_MODELVIEW);
}
//マウスの動き
void motion(int x, int y){
int xDisp, yDisp;
xDisp = x - xBegin;
yDisp = y - yBegin;
switch (mButton) {
case GLUT_LEFT_BUTTON:
azimuth += (float) xDisp/2.0;
elevation -= (float) yDisp/2.0;
break;
case GLUT_MIDDLE_BUTTON:
cameraX -= (float) xDisp/40.0;
cameraY += (float) yDisp/40.0;
break;
case GLUT_RIGHT_BUTTON:
cameraDistance += (float) xDisp/40.0;
break;
}
xBegin = x;
yBegin = y;
}
//マウスの操作
void mouse(int button, int state, int x, int y){
if (state == GLUT_DOWN) {
switch(button) {
case GLUT_RIGHT_BUTTON:
case GLUT_MIDDLE_BUTTON:
case GLUT_LEFT_BUTTON:
mButton = button;
break;
}
xBegin = x;
yBegin = y;
}
}
//視点変更
void polarview(){
glTranslatef( cameraX, cameraY, cameraDistance);
glRotatef( -twist, 0.0, 0.0, 1.0);
glRotatef( -elevation, 1.0, 0.0, 0.0);
glRotatef( -azimuth, 0.0, 1.0, 0.0);
}
//メイン
int main(int argc, char *argv[]){
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(FormWidth, FormHeight);
glutCreateWindow(argv[0]);
//コールバック
glutReshapeFunc (reshape);
glutDisplayFunc(display);
glutIdleFunc(idle);
glutMouseFunc(mouse);
glutMotionFunc(motion);
init();
glutMainLoop();
context.Shutdown();
return 0;
}
//ポイントクラウド描画
void drawPointCloud(Mat &rgbImage,Mat &pointCloud_XYZ){
static int x,y;
glPointSize(2);
glBegin(GL_POINTS);
uchar *p = rgbImage.data;
Point3f *point = (Point3f*)pointCloud_XYZ.data;
for(y = 0;y < KINECT_DEPTH_HEGIHT;y++){
for(x = 0;x < KINECT_DEPTH_WIDTH;x++,p += 3,point++){
if(point->z == 0)
continue;
glColor3ubv(p);
glVertex3f(point->x,point->y,point->z);
}
}
glEnd();
}
//3次元ポイントクラウドのための座標変換
void retrievePointCloudMap(Mat &depth,Mat &pointCloud_XYZ){
static const int size = KINECT_DEPTH_HEGIHT * KINECT_DEPTH_WIDTH;
static XnPoint3D proj[size] = {0};
static int x,y;
XnPoint3D *p = proj;
unsigned short* dp = (unsigned short*)depth.data;
for(y = 0; y < KINECT_DEPTH_HEGIHT; y++ ){
for(x = 0; x < KINECT_DEPTH_WIDTH; x++, p++, dp++){
p->X = x;
p->Y = y;
p->Z = *dp * 0.001f; // from mm to meters
}
}
//現実座標に変換
depthGenerator.ConvertProjectiveToRealWorld(size, proj, (XnPoint3D*)pointCloud_XYZ.data);
}
//人物の骨格を描画
void drawUserJoint(){
XnUserID aUsers[15];
XnUInt16 nUsers = 15;
userGenerator.GetUsers(aUsers, nUsers);
for (int i = 0; i < nUsers; ++i){
if (userGenerator.GetSkeletonCap().IsTracking(aUsers[i])){
//各関節に点を打つ
glPointSize(5);
glBegin(GL_POINTS);
glColor4f(0,255,0, 1);
for(int Joint = XN_SKEL_HEAD;Joint < XN_SKEL_RIGHT_FOOT + 1;Joint++)
DrawJoint(aUsers[i],(XnSkeletonJoint)Joint);
glEnd();
//各肢に線を書く
glBegin(GL_LINES);
glColor4f(255,0,0, 1);
DrawLimb(aUsers[i], XN_SKEL_HEAD, XN_SKEL_NECK);
DrawLimb(aUsers[i], XN_SKEL_NECK, XN_SKEL_LEFT_SHOULDER);
DrawLimb(aUsers[i], XN_SKEL_LEFT_SHOULDER, XN_SKEL_LEFT_ELBOW);
DrawLimb(aUsers[i], XN_SKEL_LEFT_ELBOW, XN_SKEL_LEFT_HAND);
DrawLimb(aUsers[i], XN_SKEL_NECK, XN_SKEL_RIGHT_SHOULDER);
DrawLimb(aUsers[i], XN_SKEL_RIGHT_SHOULDER, XN_SKEL_RIGHT_ELBOW);
DrawLimb(aUsers[i], XN_SKEL_RIGHT_ELBOW, XN_SKEL_RIGHT_HAND);
DrawLimb(aUsers[i], XN_SKEL_LEFT_SHOULDER, XN_SKEL_TORSO);
DrawLimb(aUsers[i], XN_SKEL_RIGHT_SHOULDER, XN_SKEL_TORSO);
DrawLimb(aUsers[i], XN_SKEL_TORSO, XN_SKEL_LEFT_HIP);
DrawLimb(aUsers[i], XN_SKEL_LEFT_HIP, XN_SKEL_LEFT_KNEE);
DrawLimb(aUsers[i], XN_SKEL_LEFT_KNEE, XN_SKEL_LEFT_FOOT);
DrawLimb(aUsers[i], XN_SKEL_TORSO, XN_SKEL_RIGHT_HIP);
DrawLimb(aUsers[i], XN_SKEL_RIGHT_HIP, XN_SKEL_RIGHT_KNEE);
DrawLimb(aUsers[i], XN_SKEL_RIGHT_KNEE, XN_SKEL_RIGHT_FOOT);
DrawLimb(aUsers[i], XN_SKEL_LEFT_HIP, XN_SKEL_RIGHT_HIP);
glEnd();
}
}
}
void DrawJoint(XnUserID player, XnSkeletonJoint eJoint){
XnSkeletonJointPosition joint;
userGenerator.GetSkeletonCap().GetSkeletonJointPosition(player, eJoint, joint);
//信頼性の低い場合描画しない
if (joint.fConfidence < 0.5)
return;
glVertex3f(joint.position.X* 0.001f, joint.position.Y* 0.001f, joint.position.Z* 0.001f);
}
void DrawLimb(XnUserID player, XnSkeletonJoint eJoint1, XnSkeletonJoint eJoint2){
XnSkeletonJointPosition joint1, joint2;
userGenerator.GetSkeletonCap().GetSkeletonJointPosition(player, eJoint1, joint1);
userGenerator.GetSkeletonCap().GetSkeletonJointPosition(player, eJoint2, joint2);
//信頼性の低い場合描画しない
if (joint1.fConfidence < 0.5 || joint2.fConfidence < 0.5)
return;
glVertex3f(joint1.position.X* 0.001f, joint1.position.Y* 0.001f, joint1.position.Z* 0.001f);
glVertex3f(joint2.position.X* 0.001f, joint2.position.Y* 0.001f, joint2.position.Z* 0.001f);
}
// ユーザー検出
void XN_CALLBACK_TYPE UserDetected( xn::UserGenerator& generator, XnUserID nId, void* pCookie )
{
printf("ユーザID%d 検出 %d人目\n",nId,generator.GetNumberOfUsers());
generator.GetSkeletonCap().RequestCalibration(nId, TRUE);
}
// キャリブレーションの終了
void XN_CALLBACK_TYPE CalibrationEnd(xn::SkeletonCapability& capability, XnUserID nId, XnBool bSuccess, void* pCookie)
{
if ( bSuccess ) {
printf("ユーザID%d キャリブレーション成功\n",nId);
capability.StartTracking(nId);
}
else {
printf("ユーザID%d キャリブレーション失敗\n",nId);
}
}