「技術」カテゴリーアーカイブ

【ダイエット支援】【食事管理】目標カロリーを保持、グラフに反映させる。

前回までの状況はこちら

最新ソースはこちら(gitHub)。

https://github.com/takishita2nd/diet-mng

前回計算した内容をデータベースに保存・読み出しを行う機能を実装します。

まずはデータベースのマイグレーションを作成。

class CreateEatingTarget extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('eating_targets', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->double('protein');
            $table->double('liqid');
            $table->double('carbo');
            $table->double('calorie');
            $table->timestamps();
            $table->engine = 'InnoDB';
            $table->charset = 'utf8mb4';
            $table->collation = 'utf8mb4_unicode_ci';
        });

        Schema::create('eating_target_user', function (Blueprint $table) {
            $table->integer('user_id')
                  ->foreign('user_id')
                  ->references('id')->on('users')
                  ->onDelete('cascade');
            $table->integer('eating_target_id')
                  ->foreign('eating_target_id')
                  ->references('id')->on('eating_targets')
                  ->onDelete('cascade');
            $table->engine = 'InnoDB';
            $table->charset = 'utf8mb4';
            $table->collation = 'utf8mb4_unicode_ci';
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('eating_target_user');
        Schema::dropIfExists('eating_targets');
    }
}
$ php artisan migrate

このテーブルのモデルを作成。

class EatingTarget extends Model
{
    protected $table = 'eating_targets';
    
    public function users()
    {
        return $this->belongsToMany('App\User');
    }
}

Userテーブルからのリレーションも作成します。

class User extends Authenticatable
{
    public function EatingTargets()
    {
        return $this->belongsToMany('App\Model\EatingTarget');
    }

このデータベースにアクセスするためのAPIを作成します。

セット処理。

Route::post('api/eating/settarget', 'Eating\ApiController@setTarget');
class ApiController extends Controller
{
    /**
     * 目標栄養素を設定する
     */
    public function setTarget(Request $request)
    {
        $paramNames = $this->eatingManagement->getTargetParam();
        $param = [];
        foreach($paramNames as $name) {
            $param[$name] = $request->contents[$name];
        }
        $this->eatingManagement->setTarget($param, Auth::user());
        return response()->json();
    }
use App\Model\EatingTarget;

class EatingManagementRepository
{
    private $targetParamNames = ['protein', 'liqid', 'carbo', 'calorie'];

    public function setTarget($param, $user)
    {
        $model = $user->EatingTargets()->first();
        if(is_null($model)) {
            $model = new EatingTarget();
        }

        foreach($this->targetParamNames as $name)
        {
            $model->$name = $param[$name];
        }
        $model->save();

        $this->attachToUser($model, $user);
    }

    public function getTargetParam()
    {
        return $this->targetParamNames;
    }

これをVue.jsの処理に組み込みます。

    methods: {
        clickAdd: function() {
            var self = this;
            this.param.contents = this.contents;
            axios.post('/api/eating/settarget', this.param).then(function(response){
                self.closeModal();
                self.$emit('update');
            }).catch(function(error){
                self.error_flg = true;
                self.errors = error.response.data.errors;
            });
        },

次は、このデータを読み出す処理を実装します。

グラフデータを読み出すAPIがすでにありますので、これにデータを追加します。

class ApiController extends Controller
{
    /**
     * グラフ用データを取得する
     */
    public function graph(Request $request)
    {
        return response()->json([
            'data' => $this->eatingManagement->getDaily(Auth::user(), $request->contents['date']), 
            'target' => $this->eatingManagement->getTarget(Auth::user())
            ]);
    }
class EatingManagementRepository
{
    public function getTarget($user)
    {
        return $user->EatingTargets()->first();
    }
        graphUpdate: function() {
            var ctx = document.getElementById("eating");
            var self = this;
            this.contents.date = this.todayDate;
            this.param.contents = this.contents;
            this.datasets = [];
            axios.post('api/eating/graph', this.param).then(function(response){
                if(response.data.data != null) {
                    self.datasets.push(Math.ceil(response.data.data.protein / response.data.target.protein * 100));
                    self.datasets.push(Math.ceil(response.data.data.liqid / response.data.target.liqid * 100));
                    self.datasets.push(Math.ceil(response.data.data.carbo / response.data.target.carbo * 100));
                    self.datasets.push(Math.ceil(response.data.data.calorie / response.data.target.calorie * 100));
                    var myChart = new Chart(ctx, {

なかなかいい感じなので、これで行きましょう。

【cocos2d-x】シーンを追加する。

今日はもう一個やったので、まとめておく。

シーンを追加する場合。

Cocos2d-xでは1シーンにつき1クラスと考えまして、

今回はSampleSceneを追加してみます。

C++の場合はクラスを記入したヘッダーファイルと、処理本体を記述したソースファイル本体を作成します。

中身は、とりあえずHelloWorldとほぼ同じにして、最初にSampleSceneを表示するようにします。

class SampleScene : public cocos2d::Scene
{
public:
    static cocos2d::Scene* createScene();

    virtual bool init();
    
    // a selector callback
    void menuCloseCallback(cocos2d::Ref* pSender);
    
    // implement the "static create()" method manually
    CREATE_FUNC(SampleScene);
};
#include "SampleScene.h"
#include "SimpleAudioEngine.h"

USING_NS_CC;

Scene* SampleScene::createScene()
{
    return SampleScene::create();
}

// Print useful error message instead of segfaulting when files are not there.
static void problemLoading(const char* filename)
{
    printf("Error while loading: %s\n", filename);
    printf("Depending on how you compiled you might have to add 'Resources/' in front of filenames in HelloWorldScene.cpp\n");
}

// on "init" you need to initialize your instance
bool SampleScene::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Scene::init() )
    {
        return false;
    }

    auto visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

    // add "HelloWorld" splash screen"
    auto sprite = Sprite::create("HelloWorld.png");
    if (sprite == nullptr)
    {
        problemLoading("'HelloWorld.png'");
    }
    else
    {
        // position the sprite on the center of the screen
        sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));

        // add the sprite as a child to this layer
        this->addChild(sprite, 0);
    }
    return true;
}


void SampleScene::menuCloseCallback(Ref* pSender)
{
    //Close the cocos2d-x game scene and quit the application
    Director::getInstance()->end();

    /*To navigate back to native iOS screen(if present) without quitting the application  ,do not use Director::getInstance()->end() as given above,instead trigger a custom event created in RootViewController.mm as below*/

    //EventCustom customEndEvent("game_scene_close_event");
    //_eventDispatcher->dispatchEvent(&customEndEvent);


}
    // create a scene. it's an autorelease object
    auto scene = SampleScene::createScene();

    // run
    director->runWithScene(scene);

    return true;
}

ただ、C言語やっている人なら分かると思いますが、これだけじゃあビルドしてくれません。

通常はmakefileを弄るのですが、WindowsはVisualStudio、AndroidはAndroid Studioでビルドするので、

VisualStudioの場合

プロジェクト名\proj.win32\プロジェクト名.vcxproj

のここに追加するファイルを記載する。

  <ItemGroup>
    <ClCompile Include="..\Classes\AppDelegate.cpp" />
    <ClCompile Include="..\Classes\HelloWorldScene.cpp" />
    <ClCompile Include="..\Classes\SampleScene.cpp" />
    <ClCompile Include="main.cpp" />
  </ItemGroup>
  <ItemGroup>
    <ClInclude Include="..\Classes\AppDelegate.h" />
    <ClInclude Include="..\Classes\HelloWorldScene.h" />
    <ClInclude Include="..\Classes\SampleScene.h" />
    <ClInclude Include="main.h" />
  </ItemGroup>

Androidの場合

プロジェクト名\CMakeLists.txt

のここに追加するファイルを記載する。

# add cross-platforms source files and header files 
list(APPEND GAME_SOURCE
     Classes/AppDelegate.cpp
     Classes/HelloWorldScene.cpp
     Classes/SampleScene.cpp
     )
list(APPEND GAME_HEADER
     Classes/AppDelegate.h
     Classes/HelloWorldScene.h
     Classes/SampleScene.h
     )

Mac/iOSはXcodeのメニューから追加すれば良いと思う。(知らんけど)

こういうシーンの追加って、cocosコマンドでなんとかできないんですかね?

シーンに限らず、クラス追加するときも同じか。

めんどくさい。

【Cocos2d-x】ラベルでディスプレイ情報などを表示する。

前回の記事でシーンの内容は大体分かった。

ちょっと気になるのはvisibleSizeとoriginの値。

じゃあ、visibleSizeとoriginの値をラベルに表示させちゃおう。

    auto str = String();
    str.appendWithFormat("visible (%f %f)", visibleSize.width, visibleSize.height);
    auto label = Label::createWithTTF(str.getCString(), "fonts/msgothic.ttc", 24);
    if (label == nullptr)
    {
        problemLoading("'fonts/msgothic.ttc'");
    }
    else
    {
        // position the label on the center of the screen
        label->setPosition(Vec2(origin.x + visibleSize.width/2,
                                origin.y + visibleSize.height - label->getContentSize().height));

        // add the label as a child to this layer
        this->addChild(label, 1);
    }

    auto str2 = String();
    str2.appendWithFormat("origin (%f %f)", origin.x, origin.y);
    auto label2 = Label::createWithTTF(str2.getCString(), "fonts/msgothic.ttc", 24);
    if (label2 == nullptr)
    {
        problemLoading("'fonts/msgothic.ttc'");
    }
    else
    {
        // position the label on the center of the screen
        label2->setPosition(Vec2(origin.x + visibleSize.width/2,
                                origin.y + visibleSize.height - label->getContentSize().height * 2));

        // add the label as a child to this layer
        this->addChild(label2, 2);
    }

フォントについて

フォントはResource/fontsフォルダに拡張子ttfファイルが置いてあると思いますが、

Label::createWithTTF()をコールするときにフォントファイルを指定します。

Windowsだったらフォントはc:\windows\fontsがあるので、ここにあるttf/ttcファイルをここに置けば使用することができます。

文字列について

文字列はcocos2dx::Stringというクラスが存在するらしい。

これを使った方がいろいろと便利なので、これを使用する。

addChild()の第二パラメータ

これはzIndexとあったので、重ねて表示する場合、上に表示する順番を示すパラメータですね。

数字が大きい方が上に表示されるみたいです。

Windowsでの表示結果。

visibleは画面のサイズ、originは原点の座標のようです。

Windowsはこれでいいのですが、Android(pixel4a)の場合はこうなりました。

Pixel4aはちょっと横長なので、heightが少し小さいようです。

アスペクト比が異なり、アスペクト比は長辺が基準なので、その分heightが小さいのですね。

あと、左下にピンホールカメラがあるので、その分だけ、originのy座標が少し上になっていますね。

特殊ディスプレイ、嫌い。

まぁ、この点はどうするか後で考えよう。

【北海道大戦】バトルシーンの画面を作成する

前回までの状況はこちら。

最新ソースはこちら(gitHub)。

https://github.com/takishita2nd/HokkaidoWar

バトルシーンの画面を作成していきます。

とは言っても部品を配置しているだけですが。

    class BattleScene : asd.Scene
    {
        private asd.TextObject2D _label = null;
        private asd.TextObject2D _attackCity = null;
        private asd.TextObject2D _deffenceCity = null;
        private asd.TextureObject2D _image_gu_attack = null;
        private asd.TextureObject2D _image_choki_attack = null;
        private asd.TextureObject2D _image_par_attack = null;
        private asd.TextureObject2D _image_gu_deffence = null;
        private asd.TextureObject2D _image_choki_deffence = null;
        private asd.TextureObject2D _image_par_deffence = null;
        private asd.TextObject2D _attackParam = null;
        private asd.TextObject2D _deffenceParam = null;

        public BattleScene()
        {

        }

        protected override void OnRegistered()
        {
            var layer = new asd.Layer2D();
            AddLayer(layer);

            // 下地
            var background = new asd.GeometryObject2D();
            layer.AddObject(background);
            var bgRect = new asd.RectangleShape();
            bgRect.DrawingArea = new asd.RectF(0, 0, 1800, 1000);
            background.Shape = bgRect;

            _label = new asd.TextObject2D();
            _label.Font = Singleton.GetLargeFont();
            _label.Text = "VS";
            _label.Position = new asd.Vector2DF(470, 400);
            layer.AddObject(_label);

            _attackCity = new asd.TextObject2D();
            _attackCity.Font = Singleton.GetLargeFont();
            _attackCity.Text = "札幌";
            _attackCity.Position = new asd.Vector2DF(450, 150);
            layer.AddObject(_attackCity);

            _deffenceCity = new asd.TextObject2D();
            _deffenceCity.Font = Singleton.GetLargeFont();
            _deffenceCity.Text = "小樽";
            _deffenceCity.Position = new asd.Vector2DF(450, 650);
            layer.AddObject(_deffenceCity);

            _attackParam = new asd.TextObject2D();
            _attackParam.Font = Singleton.GetLargeFont();
            _attackParam.Text = "戦闘力:10000";
            _attackParam.Position = new asd.Vector2DF(700, 650);
            layer.AddObject(_attackParam);

            _deffenceParam = new asd.TextObject2D();
            _deffenceParam.Font = Singleton.GetLargeFont();
            _deffenceParam.Text = "戦闘力:8000";
            _deffenceParam.Position = new asd.Vector2DF(700, 150);
            layer.AddObject(_deffenceParam);

            _image_gu_attack = new asd.TextureObject2D();
            _image_gu_attack.Texture = Singleton.GetImageGu();
            _image_gu_attack.Position = new asd.Vector2DF(300, 500);
            layer.AddObject(_image_gu_attack);

            _image_choki_attack = new asd.TextureObject2D();
            _image_choki_attack.Texture = Singleton.GetImageChoki();
            _image_choki_attack.Position = new asd.Vector2DF(450, 500);
            layer.AddObject(_image_choki_attack);

            _image_par_attack = new asd.TextureObject2D();
            _image_par_attack.Texture = Singleton.GetImagePar();
            _image_par_attack.Position = new asd.Vector2DF(600, 500);
            layer.AddObject(_image_par_attack);

            _image_gu_deffence = new asd.TextureObject2D();
            _image_gu_deffence.Texture = Singleton.GetImageGu();
            _image_gu_deffence.Position = new asd.Vector2DF(300, 250);
            layer.AddObject(_image_gu_deffence);

            _image_choki_deffence = new asd.TextureObject2D();
            _image_choki_deffence.Texture = Singleton.GetImageChoki();
            _image_choki_deffence.Position = new asd.Vector2DF(450, 250);
            layer.AddObject(_image_choki_deffence);

            _image_par_deffence = new asd.TextureObject2D();
            _image_par_deffence.Texture = Singleton.GetImagePar();
            _image_par_deffence.Position = new asd.Vector2DF(600, 250);
            layer.AddObject(_image_par_deffence);
        }

        protected override void OnUpdated()
        {

        }
    }

シンプルだけどまぁいいでしょう。

グー・チョキ・パーは絵文字が使えないっぽいので、絵文字を拡大してスクリーンショットで画像を作成しています。

次回は実際にシーンの切り替えをやってみます。

【ラズパイ】カメラモジュールを使ってみる。

カメラモジュール買いました。

装着。

今回は足つきで固定できるタイプの物を買いました。

フラットケーブルで接続したので、ラズパイの設定でカメラを有効にすれば使用できるはずです。

カメラで静止画を撮影するには以下のコマンドを入力します。

$ raspistill -o image.jpg

モニターにプレビューが表示され、それが閉じるのと同時に、カレントディレクトリにimage.jpgが保存されます。

動画を撮影するときは以下のコマンドを入力します。

$ raspivid -t 5000 -o test.h264

プレビューのみの時は以下です。

$ raspivid --demo 5000

ただこれだけできても面白くない。

次回はプログラムから画像データを扱ってみたいと思います。

【ダイエット支援】【食事管理】目標カロリーを計算する

前回までの状況はこちら。

最新ソースはこちら(gitHub)。

https://github.com/takishita2nd/diet-mng

やっぱり、前回書いたとおり、きちんと目標となるカロリーを計算しないと、正しいグラフ書けない、ということで、ダイアログを追加します。

<template>
    <div>
        <div id="overlay" v-show="show">
            <div id="content">
                <p v-if="error_flg == true" class="error">
                    <ui>
                        <li v-for="error in errors">{{ error }}</li>
                    </ui>
                </p>
                <table class="edit">
                    <tbody>
                        <tr>
                            <td>身長</td>
                            <td><input type="number" v-model="inputParameter.height" /></td>
                        </tr>
                        <tr>
                            <td>体重</td>
                            <td><input type="number" v-model="inputParameter.weight" /></td>
                        </tr>
                        <tr>
                            <td>年齢</td>
                            <td><input type="number" v-model="inputParameter.age" /></td>
                        </tr>
                        <tr>
                            <td>アクティブ度</td>
                            <td>
                                <select name="active" v-model="inputParameter.active">
                                    <option value="1" selected>低</option>
                                    <option value="2">中</option>
                                    <option value="3">高</option>
                                </select>
                            </td>
                        </tr>
                        <tr>
                            <td>目的</td>
                            <td>
                                <select name="target" v-model="inputParameter.target">
                                    <option value="1" selected>維持</option>
                                    <option value="2">減量</option>
                                    <option value="3">増量</option>
                                </select>
                            </td>
                        </tr>
                    </tbody>
                </table>
                <p />
                <table class="edit">
                    <tbody>
                        <tr>
                            <td>目標カロリー</td>
                            <td>{{calorie}} cal</td>
                        </tr>
                        <tr>
                            <td>タンパク質</td>
                            <td>{{protein}} g</td>
                        </tr>
                        <tr>
                            <td>脂質</td>
                            <td>{{liquid}} g</td>
                        </tr>
                        <tr>
                            <td>炭水化物</td>
                            <td>{{carbo}} g</td>
                        </tr>
                    </tbody>
                </table>
                <p id="command">
                    <button @click="clickAdd">入力</button>
                    <button @click="closeModal">閉じる</button>
                </p>
            </div>
        </div>
    </div>
</template>
<script>
export default {
    props: ['show'],
    data() {
        return {
            errors: [],
            error_flg: [],
            param: {},
            inputParameter: {
                height: 0,
                weight: 0,
                age: 0,
                active: 1,
                target: 1,
            },
            contents: {
                calorie: 0,
                protein: 0,
                liquid: 0,
                carbo: 0,
            },
        };
    },
    created: function() {
        this.clear();
    },
    methods: {
        clickAdd: function() {
            var self = this;
            this.param.contents = this.contents;
            axios.post('/api/eating/settarget', this.param).then(function(response){
            }).catch(function(error){
                self.error_flg = true;
                self.errors = error.response.data.errors;
            });
        },
        closeModal: function() {
            this.$parent.showCalcCalorieContent = false;
        },
        clear: function() {
            this.inputParameter.height = 0;
            this.inputParameter.weight = 0;
            this.inputParameter.age = 0;
            this.inputParameter.active = "1";
            this.inputParameter.target = "1";
            this.contents.calorie = 0;
            this.contents.protein = 0;
            this.contents.liquid = 0;
            this.contents.carbo = 0;
            this.error_flg = false;
            this.errors = [];
        }
    }
}
</script>

ボタンはここに配置ました。

身長、体重、年齢と、アクティブ度と目標を入力してもらって、目標となるカロリーと摂取栄養素の量を計算します。

計算式はそんなに難しくないので、フロントエンド側で計算して表示させちゃいます。

    computed: {
        calorie: function() {
            var cal = 10 * this.inputParameter.weight + 6.25 * this.inputParameter.height - 5 * this.inputParameter.age + 5;
            var k = 1;
            // アクティブ度の計算
            switch(this.inputParameter.active){
                case "1": k = 1.2; break;
                case "2": k = 1.55; break;
                case "3": k = 1.725; break;
            }
            cal = cal * k;
            // 目標の計算
            switch(this.inputParameter.target){
                case "1": k = 1; break;
                case "2": k = 0.8; break;
                case "3": k = 1.2; break;
            }
            this.contents.calorie = Math.ceil(cal * k);
            return this.contents.calorie;
        },
        protein: function() {
            this.contents.protein = this.inputParameter.weight * 2;
            return this.contents.protein;
        },
        liquid: function() {
            this.contents.liquid = Math.ceil(this.contents.calorie * 0.25 / 9);
            return this.contents.liquid;
        },
        carbo: function() {
            this.contents.carbo = Math.ceil((this.contents.calorie - this.contents.protein * 4 - this.contents.liquid * 9) / 4);
            return this.contents.carbo;
        },
    },

まず、computedに記載することで、入力パラメータからすぐさま計算処理を行い、結果を反映してくれます。

カロリーの計算式は、

10×体重(g)+6.25×身長(cm)-5×年齢(歳)+5

これにアクティブ度と目的に合わせて係数を掛けます。

アクティブ度

  • 低 カロリー×1.2
  • 中 カロリー×1.55
  • 高 カロリー×1.725

目的

  • 減量 カロリー×0.8
  • 維持 カロリー×1
  • 増量 カロリー×1.2

これらは以下で紹介した本の中にあります。

フロントエンド側はこれで完成。

次はこの計算結果を保持するデータベース周りのバックエンド側を作成していきます。

【ANDROID】【実質北海道一周】残りの表示部分を実装する。【完成】

前回までの状況はこちら。

最新ソースはこちら

https://github.com/takishita2nd/AroundHokkaido

残りの、数字を表示する部分を作成していきます。

完成までもう少しです。

必要なデータは、

全体の距離(km)と現在位置(km)

区間の距離(km)と現在位置(km)

パーセンテージは上の数字があれば計算で出せます。

全体の距離はjsonを読み込んだときに計算して保持っておくのが良いでしょう。

全体の距離はすでに覚醒済み。

区間のデータはgetPosition()で取得させるのが良いでしょう。

class StartEndPosition(startCity: String, endCity: String, position: Double, segment: Double) {
val startCityName : String = startCity
val endCityName : String = endCity
val positionDistance: Double = position
val segmentDistance: Double = segment
}
    fun getPosition() : StartEndPosition {
var tempDistance = 0.0
var start : String = ""
var end : String = ""
var loop : Boolean = false
var segment: Double = 0.0
var aaa: Double = 0.0
run {
citylist.cityList.forEach{
if(loop){
end = it.city
return@run
}else{
tempDistance += it.distance
if(resultDistance < tempDistance){
start = it.city
aaa = resultDistance - (tempDistance - it.distance)
segment = it.distance
loop = true
}
}
}
}
return StartEndPosition(start, end, aaa, segment)
}
}
distanceFromStart.text = distancefromSapporoFormat.format(aroundHokkaido.getResultDistance(),
aroundHokkaido.getResultDistance() / aroundHokkaido.getTotalDistance() * 100)
distanceSection.text = distanceFormat.format(startEnd.positionDistance,
startEnd.positionDistance / startEnd.segmentDistance * 100)

とりあえず完成だけど、ぶっちゃけこのアプリ自体、そんなに面白くないなぁ。

まぁ、一通りどんな感じでコーディングするのか、それを経験するのには良かったのではないでしょうか。

これとは違うけど、別アプリも考えてみようと思います。

【cocos2d-x】プロジェクト作成直後のソースファイルを解析する。

Cocos2d-xの開発環境作成についてはこちらにまとめてあります。

https://qiita.com/takishita2nd/items/0b54af9860f54c65fd24

今回は、プロジェクト作成直後に作成されるソースファイルの中身を覗いてみます。

とはいっても、いきなりソース解析も難しいので、こちらの初心者用の解説を見ながら確認していきました。

https://www.tuyano.com/index3?id=9496003

基本的に弄るソースはClassesの中だけです。

それ以外はCocos2d-xのライブラリ本体だったり、各プラットフォームのビルド環境なので、今後一切触る必要は無いと思います。

むしろ、書き換えたら正常に動く保証はない。

AppDelegateクラス

アプリケーション全体の設定を記述します。

タスク切り替え時の処理とかをここに書くことになります。

最初は弄ることは無いと思います。

HelloWorldクラス

上のリンクにあった初心者用テキストには、Layerクラスを継承してHelloWorldクラスを作成していると書いてありますが、実際にはSceneクラスを継承しています。

class HelloWorld : public cocos2d::Scene
{
public:
    static cocos2d::Scene* createScene();

    virtual bool init();
    
    // a selector callback
    void menuCloseCallback(cocos2d::Ref* pSender);
    
    // implement the "static create()" method manually
    CREATE_FUNC(HelloWorld);
};

Cocos2d-xの概念として、画面毎にシーンを作成し、その中に画面を構成するオブジェクトを配置する、というイメージのようです。

大きな差分はこれくらいかな。

init()で画面に配置する部品をオブジェクト化して配置しています。

青の部分はメニューになっていて、クリックするとアプリを終了するコールバックが呼ばれます。

赤い部分はフォントを読み込んでテキストを表示しています。

黄色の部分はResourceフォルダの中にある画像を表示させています。

でもイマイチ座標周りがよく分からん。

visibleSizeが画面のサイズで、

originがOpenGLの座標系?を表している?

画面の解像度が変わっても表示が崩れないように計算していると思うのだが。

でも、ここまでで、表示位置を変えることはできると思うぞ。

ほらできた。

【北海道大戦】シーンを追加する

最新ソースはこちら(gitHub)。

https://github.com/takishita2nd/HokkaidoWar

バトルシーンを追加するに当たりまして、

今まではエンジンに直接オブジェクトを配置していましたが、

シーンを使用するとなると、このやり方では上手くいけないっぽい。

なので、メインシーンを追加して、このシーンのレイヤーにオブジェクトを配置する必要があります。

なので、今回はその修正を実行。

    class HokkaidoWar
    {

        public HokkaidoWar()
        {
        }

        public void Run()
        {
            asd.Engine.Initialize("北海道大戦", 1200, 1000, new asd.EngineOption());

            // シーンの登録
            var scene = new MainScene();
            asd.Engine.ChangeScene(scene);

            while (asd.Engine.DoEvents())
            {
                asd.Engine.Update();
            }
            asd.Engine.Terminate();
        }

    }

いままでエンジンに追加していたオブジェクトは全て無くなり、シーンを作成して、そのシーンに移行、という処理に変わります。

    class MainScene : asd.Scene
    {
中略
        protected override void OnRegistered()
        {
            var layer = Singleton.GetMainSceneLayer();
            AddLayer(layer);

            // 下地
            var background = new asd.GeometryObject2D();
            layer.AddObject(background);
            var bgRect = new asd.RectangleShape();
            bgRect.DrawingArea = new asd.RectF(0, 0, 1800, 1000);
            background.Shape = bgRect;

            cities = new List<City>();
            var r = new Random();
            foreach (var map in mapData.list)
            {
                City city = new City(map.name, map.point, map.population);
                cities.Add(city);
            }

            _battle = new Battle(cities);
            aliveCities = _battle.GetAliveCityList();
        }

        protected override void OnUpdated()
        {
            asd.Vector2DF pos = asd.Engine.Mouse.Position;

            switch (gameStatus)
            {
                case GameStatus.SelectCity:
                    cycleProcessSelectCity(pos);
                    break;
                case GameStatus.ActionEnemy:
                    cycleProcessActionEnemy(pos);
                    break;
                case GameStatus.ActionPlayer:
                    cycleProcessActionPlayer(pos);
                    break;
                case GameStatus.ShowResult:
                    break;
                case GameStatus.GameEnd:
                    cycleProcessGameEnd();
                    break;
                case GameStatus.GameOver:
                    cycleProcessGameOver(pos);
                    break;
            }

            if (asd.Engine.Mouse.LeftButton.ButtonState == asd.ButtonState.Push)
            {
                switch (gameStatus)
                {
                    case GameStatus.SelectCity:
                        onClickMouseSelectCity(pos);
                        break;
                    case GameStatus.ActionEnemy:
                        break;
                    case GameStatus.ActionPlayer:
                        onClickMouseActionPlayer(pos);
                        break;
                    case GameStatus.ShowResult:
                        onClickMouseShowResult();
                        break;
                    case GameStatus.GameEnd:
                        break;
                    case GameStatus.GameOver:
                        break;
                }
            }
        }

後略

OnRegistered()はシーン登録時に実行されます。

オブジェクトの配置はここで行います。

レイヤーはシングルトンで取り出すようにしました。(いろんなところでオブジェクトの配置やっているので)

OnUpdated()には、while (asd.Engine.DoEvents()){ }内で実行していた処理を行います。

これは、asd.Engine.Update();実行時に実行されるイメージです。

細かい所は結構修正しているのですが、概要はこんな感じです。

とりあえず、シーンを使用して、今までと同じ動きが出来ることを確認しました。

次はバトルシーンの追加をしていきましょうか。

ワードプレスのログイン画面にアクセス出来ないようにする

このままじゃやばいことは知ってた。

ログイン画面が表示できてしまうと、そこから簡単に侵入できてしまうかもしれない。

なので、セキュリティを高めるために、ログイン画面を別のURLに移動させて、ブログから見えないようにします。

まず、ログイン画面へのリンクですが、これはウィジェットのメタ情報を削除すると消えます。

続けて、ログイン画面のURLを変える方法。

これはプラグインを使う方が簡単です。

プラグインの新規追加から「WPS Hide Login」を検索し、ヒットしたプラグインをインストール、有効化。

プラグインの設定でログイン画面に遷移するためのクエリパラメータを設定することが出来るので、自分しか分からないパラメータに変更して保存。

ログイン画面へのURLが表示されるので、必ずブックマークに保存してください。

これでワードプレスは安全になりました!