最近Kindleで購入した書籍『JavaScriptゲームプログラミング 知っておきたい数学と物理の基本』から、2-1-1 X-Y座標系と円座標系をやってみました。
JavaScriptゲームプログラミング 知っておきたい数学と物理の基本 (Future Coders(NextPublishing))
- 作者: 田中賢一郎
- 出版社/メーカー: インプレスR&D
- 発売日: 2017/03/24
- メディア: Kindle版
- この商品を含むブログを見る
極座標系から三角関数を用いて直交座標系を簡単に求められますよという内容になります。三角形の内角θによってXをcosθ、Yをsinθで求めることができます。その関係性をHTML上のCanvasに表現しています。
実行例
実行している動画になります。スライダーを動かすと角度に応じて、XとYが変化していく様子が確認できます。
TypeScriptでのコード
そのままJavaScriptコードを転記すると丸パクリになってしまうので、TypeScriptで実装してみました。
実装及びデバッグ環境は以下の通りです。
- Windows10
- VisualStudio2017 + TypeScriptテンプレートプロジェクト
- IIS Express + Chorome
// TypeScript側のコード class Trig { private ctx: CanvasRenderingContext2D = null; init(): void { var canvas: HTMLCanvasElement = <HTMLCanvasElement>document.getElementById("graph"); this.ctx = canvas.getContext("2d"); this.ctx.font = "24px sans-serif"; this.paint(0); } update(): void { var degree: string = (<HTMLInputElement>document.getElementById("theta")).value; document.getElementById("degree").textContent = degree; this.paint(+degree); } drawLine(x0: number, y0: number, x1: number, y1: number, color: string): void { this.ctx.strokeStyle = color; this.ctx.beginPath(); this.ctx.moveTo(x0, y0); this.ctx.lineTo(x1, y1); this.ctx.closePath(); this.ctx.stroke(); } paint(degree: number): void { this.ctx.fillStyle = "white"; this.ctx.fillRect(0, 0, 600, 600); this.ctx.save(); this.ctx.translate(300, 300); // 座標軸 this.ctx.strokeStyle this.drawLine(0, -300, 0, 300, "black"); this.drawLine(-300, 0, 300, 0, "black"); var s0: number = Math.sin(degree * Math.PI / 180); var c0: number = Math.cos(degree * Math.PI / 180); var c: number = c0 * 200; var s: number = s0 * -200; this.drawLine(0, 0, c, s, "red"); this.ctx.arc(0, 0, 200, 0, Math.PI * 2); this.ctx.stroke(); // ラベル this.drawLine(c, s, c, 0, "blue"); this.ctx.strokeText("con:" + c0.toFixed(3), c - 10, 20); this.drawLine(c, s, 0, s, "green"); this.ctx.strokeText("sin:" + s0.toFixed(3), -40, s - 10); this.ctx.restore(); } } var trig = new Trig(); window.onload = () => { trig.init(); document.getElementById("theta").onchange = function () { trig.update(); } };
// HTML側のコード <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Trig Function Graph</title> <script src="app.js"></script> </head> <body> <input id="theta" type="range" min="0" max="360" value="0"/>中心角=<span id="degree">0</span>度<br/> <canvas id="graph" width="600" height="600"></canvas> </body> </html>
C++/Cocos2d-xでの実装
次にC++/Cocos2d-xで同じ内容を実装してみました。
動かしたときの表示は以下のようになります。少し見ずらい場合リンク先で確認ください。
以下コードになります。同じ実装ですが少し長めになります。
// HelloWorldScene.h #pragma once #include "cocos2d.h" #include "MessageDialog.h" #include "HpBar.h" class HelloWorld : public cocos2d::Layer { // 各Nodeへアクセスするためのタグ static const int TAG_HYPO; static const int TAG_SIN_LINE; static const int TAG_COS_LINE; static const int TAG_DEGREE_TEXT; static const int TAG_SIN_LABEL; static const int TAG_COS_LABEL; int degree = 0; // 角度 public: // いつもの static cocos2d::Scene* createScene(); virtual bool init(); void menuCloseCallback(cocos2d::Ref* pSender); CREATE_FUNC(HelloWorld); // 描画の更新 void paint(double xCenter, double yCenter); }; class NodeUtil { public: // タグに対応するノードがあれば削除 static void RemoveNodeBag(cocos2d::Node* parent, int tag) { cocos2d::Node* target = parent->getChildByTag(tag); if (target == nullptr) { return; } target->removeFromParentAndCleanup(true); } };
// HelloWorldScene.cpp //#pragma execution_character_set("utf-8") #include <stdlib.h> #include <vector> #include "HelloWorldScene.h" #include <cmath> using namespace cocos2d; using namespace cocos2d::ui; const int HelloWorld::TAG_HYPO = 0; const int HelloWorld::TAG_SIN_LINE = 1; const int HelloWorld::TAG_COS_LINE = 2; const int HelloWorld::TAG_DEGREE_TEXT = 3; const int HelloWorld::TAG_SIN_LABEL = 4; const int HelloWorld::TAG_COS_LABEL = 5; Scene* HelloWorld::createScene() { // 'scene' is an autorelease object auto scene = Scene::create(); // 'layer' is an autorelease object auto layer = HelloWorld::create(); // add layer as a child to scene scene->addChild(layer); // return the scene return scene; } // on "init" you need to initialize your instance bool HelloWorld::init() { ////////////////////////////// // 1. super init first if (!Layer::init()) { return false; } Size visibleSize = Director::getInstance()->getVisibleSize(); double xCenter = visibleSize.width / 2; double yCenter = visibleSize.height / 2; Label* degreeLabel = Label::create(); degreeLabel->setPosition(Vec2(xCenter, yCenter + 170)); degreeLabel->setTag(TAG_DEGREE_TEXT); degreeLabel->setString("中心角 = " + std::to_string(this->degree) + "度"); this->addChild(degreeLabel); // X軸 DrawNode* xLine = DrawNode::create(); xLine->drawSegment(Vec2(xCenter - 150, yCenter), Vec2(xCenter + 150, yCenter), 0.25, Color4F::WHITE); this->addChild(xLine); // Y軸 DrawNode* yLine = DrawNode::create(); yLine->drawSegment(Vec2(xCenter, yCenter - 150), Vec2(xCenter, yCenter + 150), 0.25, Color4F::WHITE); this->addChild(yLine); // 円 DrawNode* circle = DrawNode::create(); circle->drawCircle(Vec2(xCenter, yCenter), 125, 0, 360, false, 1, 1, Color4F::RED); this->addChild(circle); Label* sinLabel = Label::create(); sinLabel->setColor(Color3B::GREEN); sinLabel->setTag(TAG_SIN_LABEL); sinLabel->setAnchorPoint(Vec2::ANCHOR_BOTTOM_LEFT); this->addChild(sinLabel); Label* cosLabel = Label::create(); cosLabel->setColor(Color3B::BLUE); cosLabel->setTag(TAG_COS_LABEL); cosLabel->setAnchorPoint(Vec2::ANCHOR_BOTTOM_LEFT); this->addChild(cosLabel); this->paint(xCenter, yCenter); // タッチしたときの動作 // 画面の右半分をタップで角度を+1度、左半分をタップは-1度 EventListenerTouchOneByOne* evListener = EventListenerTouchOneByOne::create(); evListener->onTouchBegan = [this, xCenter, yCenter](Touch* touch, Event* event) { if (touch->getLocation().x > Director::getInstance()->getVisibleSize().width / 2) { this->degree++; } else { this->degree--; } this->paint(xCenter, yCenter); return false; }; _eventDispatcher->addEventListenerWithSceneGraphPriority(evListener, this); return true; } // 表示している各要素の更新 void HelloWorld::paint(double xCenter, double yCenter) { double s0 = sin(CC_DEGREES_TO_RADIANS(this->degree)); double c0 = cos(CC_DEGREES_TO_RADIANS(this->degree)); double s = s0 * 125; double c = c0 * 125; // 新しい斜辺を配置 NodeUtil::RemoveNodeBag(this, TAG_HYPO); DrawNode* hypo = DrawNode::create(); hypo->drawSegment(Vec2(xCenter, yCenter), Vec2(xCenter + c, yCenter + s), 0.25, Color4F::RED); hypo->setTag(TAG_HYPO); this->addChild(hypo); // Sinを示す緑色の線 NodeUtil::RemoveNodeBag(this, TAG_SIN_LINE); DrawNode* sinLine = DrawNode::create(); sinLine->drawSegment(Vec2(xCenter, yCenter + s), Vec2(xCenter + c, yCenter + s), 0.25, Color4F::GREEN); sinLine->setTag(TAG_SIN_LINE); this->addChild(sinLine); // Cosを示す青色の線 NodeUtil::RemoveNodeBag(this, TAG_COS_LINE); DrawNode* cosLine = DrawNode::create(); sinLine->drawSegment(Vec2(xCenter + c, yCenter), Vec2(xCenter + c, yCenter + s), 0.25, Color4F::BLUE); cosLine->setTag(TAG_COS_LINE); this->addChild(cosLine); Label* degreeLabel = (Label*)this->getChildByTag(TAG_DEGREE_TEXT); degreeLabel->setString("中心角 = " + std::to_string(this->degree) + "度"); int dec, sign; Label* sinLabel = (Label*)this->getChildByTag(TAG_SIN_LABEL); sinLabel->setPosition(Vec2(xCenter, yCenter + s + 5)); sinLabel->setString("sin:" + std::to_string(s0)); Label* cosLabel = (Label*)this->getChildByTag(TAG_COS_LABEL); cosLabel->setPosition(Vec2(xCenter + c + 5, yCenter)); cosLabel->setString("sin:" + std::to_string(c0)); }
これ、自分がポンコツってだけだと思いますが、C++は3倍くらいコード書かないといけない羽目になりました。