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

【デザインパターン】Compositeパターン

Compositeパターンのサンプルコードです。

package org.example.composite;

public abstract class Component {
    public abstract void method1();
    public abstract void method2();
    protected void add(Component component){}
}
package org.example.composite;

public class Leaf extends Component{
    @Override
    public void method1() {

    }

    @Override
    public void method2() {

    }
}
package org.example.composite;

import java.util.ArrayList;
import java.util.List;

public class Composite extends Component{
    private List<Component> ComponentList = new ArrayList<>();

    @Override
    public void method1() {

    }

    @Override
    public void method2() {

    }

    @Override
    public void add(Component component) {
        ComponentList.add(component);
    }
}
package org.example.composite;

public class Main {
    public static void main(String[] args) {
        Component parent1 = new Composite();
        Component parent2 = new Composite();
        Component leaf1 = new Leaf();
        Component leaf2 = new Leaf();

        parent2.add(leaf1);
        parent2.add(leaf2);
        parent1.add(parent2);
    }
}

Compositeパターンは木構造を構成してオブジェクトを管理し、再帰的に使用したい場合に使用します。

Compositeクラスが木構造の親クラス、Leafクラスが木構造の末端のクラスです。どちらもComponentクラスを継承します。

Leafクラスでも使用するため、Componentクラスのaddメソッドはprotectedの処理無しで作成します。

実際に追加する処理はCompositeクラスのaddメソッドをオーバーライドして作成します。

【北海道大戦2021】マップの作成

とりあえず、マップを配置。

https://github.com/takishita2nd/HokkaidoWar/tree/2021_develop

こちらのサイトのフリー素材を使用しました。

https://map.finemakeyuri.com/map/101.html

マップデータはこんな感じで作成しようと思います。(データは仮)

{
    "citydata":[
        {
            "id" : 1,
            "name" : "礼文",
            "population" : 2576,
            "money" : 0,
            "point" : { "x" :21, "y" :0 },
            "link" : [2, 3]
        },
        {
            "id" : 2,
            "name" : "利尻",
            "population" : 2087,
            "money" : 0,
            "point" : { "x" :21, "y" :0 },
            "link" : [1, 3]
        },
        {
            "id" : 3,
            "name" : "利尻富士",
            "population" : 2519,
            "money" : 0,
            "point" : { "x" :21, "y" :0 },
            "link" : [1, 2]
        }
    ]
}

これを「Jsonをクラスで貼り付け」すれば、適切なクラスを作成してくれます。

    public class MapData
    {
        public Citydata[] citydata { get; set; }
    }

    public class Citydata
    {
        public int id { get; set; }
        public string name { get; set; }
        public int population { get; set; }
        public int money { get; set; }
        public Point point { get; set; }
        public int[] link { get; set; }
    }

    public class Point
    {
        public int x { get; set; }
        public int y { get; set; }
    }

これでJsonの読み込みはできるはず。

        private const string _filename = "hokkaido.json";
        public static MapData Load()
        {
            string json;
            using (var stream = new StreamReader(_filename, true))
            {
                json = stream.ReadToEnd();
            }
            return JsonConvert.DeserializeObject<MapData>(json);
        }

そして、下地の北海道の描画。

        public MainScene()
        {
        }

        protected override void OnRegistered()
        {
            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, 1900, 1000);
            background.Shape = bgRect;
            var hokkaido = new asd.TextureObject2D();
            hokkaido.Texture = asd.Engine.Graphics.CreateTexture2D("101.png");
            hokkaido.Scale = new asd.Vector2DF(1.5f, 1.5f);
            layer.AddObject(hokkaido);

さて、次はマップに都市を貼り付けるのをやっていきます。

【ラズパイ】赤外線信号の学習

えっと、

配線を間違えたせいで、赤外線受信モジュールが壊れました。

まぁ、もう一個あったので、そちらに交換しましたが。

そして、もう一つ、

前回のやり方だと、TVが反応してくれるコマンドもあれば、反応してくれないコマンドもありました。

むしろ、反応してくれないコマンドの方が多い。

なので、やり方を変えます。

こちらの記事を参考にしました。

https://qiita.com/amaebi-tabetai/items/211b9c6f751c96ff1fd7

irrecordというコマンドを使用して、赤外線のデータを何回か入力させて、正しい信号に直してくれることができるみたいです。

irrecord --disable-namespace -f -d /dev/lirc1 --driver default ~/lircd.conf

受信機をlirc1にしているので、こちらを使用します。

pi@raspberrypi:~ $ irrecord --disable-namespace -f -d /dev/lirc1 --driver default ~/lircd.
conf
Using raw access on device /dev/lirc1

irrecord -  application for recording IR-codes for usage with lirc
Copyright (C) 1998,1999 Christoph Bartelmus(lirc@bartelmus.de)

This program will record the signals from your remote control
and create a config file for lircd.

A proper config file for lircd is maybe the most vital part of this
package, so you should invest some time to create a working config
file. Although I put a good deal of effort in this program it is often
not possible to automatically recognize all features of a remote
control. Often short-comings of the receiver hardware make it nearly
impossible. If you have problems to create a config file READ THE
DOCUMENTATION at https://sf.net/p/lirc-remotes/wiki

If there already is a remote control of the same brand available at
http://sf.net/p/lirc-remotes you might want to try using such a
remote as a template. The config files already contains all
parameters of the protocol used by remotes of a certain brand and
knowing these parameters makes the job of this program much
easier. There are also template files for the most common protocols
available. Templates can be downloaded using irdb-get(1). You use a
template file by providing the path of the file as a command line
parameter.

Please take the time to finish the file as described in
https://sourceforge.net/p/lirc-remotes/wiki/Checklist/ an send it
to  <lirc@bartelmus.de> so it can be made available to others.

Press RETURN to continue.

Checking for ambient light  creating too much disturbances.
Please don't press any buttons, just wait a few seconds...

No significant noise (received 0 bytes)

Enter name of remote (only ascii, no spaces) :
Enter name of remote (only ascii, no spaces) :tv
Using tv.lircd.conf as output filename

Now start pressing buttons on your remote control.

It is very important that you press many different buttons randomly
and hold them down for approximately one second. Each button should
generate at least one dot but never more than ten dots of output.
Don't stop pressing buttons until two lines of dots (2x80) have
been generated.

Press RETURN now to start recording.
................................................................................

Please enter the name for the next button (press <ENTER> to finish recording)
channel1

Now hold down button "channel1".
Timeout (10 seconds), try again (29 retries left).

Now hold down button "channel1".

Please enter the name for the next button (press <ENTER> to finish recording)


Successfully written config file tv.lircd.conf

Press RETURN now to start recording.のところで、学習するボタンをひたすら押し続ける。

それが終われば、コマンド名に名前をつけて終了。

作成されたconfファイルには、こんな感じになっていました。

      begin raw_codes

          name channel1
             9082    4465     624     508     619     514
              621     514     622     515     621     515
              596     541     621    1645     596     541
              640    1636     588    1671     621    1645
              595    1671     596    1683     585    1671
              594     543     564    1703     566    1703
              593     548     560     571     565     573
              563     571     564     572     564     572
              564     572     563     572     563    1708
              567    1696     564    1702     564    1708
              560    1702     576    1692     565    1700
              565

      end raw_codes

これを、前回と同じように/etc/lirc/lircd.conf.d/tv.confに記入して、lircdを再起動。

これで、コマンドが動くことを確認しました。

こっちのやり方の方が確実かもしれない。

【COCOS2D-X】new演算子でインスタンスを作成してはならない(と思う)

前回のソースから、またいろいろとコーディングしていたんですが、

そこで一つ思ったことがありまして、

前回追加作成したクラス、いつdeleteすればいい??

シーンの処理の中でインスタンスを作成したのですが、

シーンが終了したあと、インスタンスを削除するタイミングが無いんです。

これは大問題で、

確実にメモリリークの問題が発生します。

と、言うことは、だよ。

cocos2d-xでは、シーンの中でnewを使ってインスタンスを作ってはいけないんじゃ無いか、って思いました。

マズいな。

これから大規模修正入りまーす。

【北海道大戦2021】開発計画

前回とは以下の点を変更したいと思います。

マップをタイルを並べて作成→地図上に都市を配置して隣接関係を線で結ぶ。

データの持ち方は後々考える。

人口データは2021年度のデータを使用します。

お金的なものを導入したい。

例えば小さな村でもお金があれば戦力補強できるような仕組みにしたい。

どのデータを使用するかは検討します。

このページに財政状況がわかるデータがありました。

http://www.pref.hokkaido.lg.jp/ss/scs/zaisei/shi-zaisei-2-2.htm

【デザインパターン】Bridgeパターン

Bridgeパターンのサンプルコードです。

package org.example.bridge;

public abstract class Implementor {
    public abstract void implMethodX();
    public abstract void implMethodY();
}
package org.example.bridge;

public class concreteImplementorA extends Implementor{
    @Override
    public void implMethodX() {

    }

    @Override
    public void implMethodY() {

    }
}
package org.example.bridge;

public class concreteImplementorB extends Implementor{
    @Override
    public void implMethodX() {

    }

    @Override
    public void implMethodY() {

    }
}
package org.example.bridge;

public class Abstraction {
    Implementor implementor;

    public Abstraction(Implementor implementor)
    {
        this.implementor = implementor;
    }

    public void method1()
    {

    }

    public void method2()
    {

    }
}
package org.example.bridge;

public class RefileAbstraction extends Abstraction{
    public RefileAbstraction(Implementor implementor) {
        super(implementor);
    }

    @Override
    public void method1()
    {

    }

    @Override
    public void method2()
    {

    }

    public void refineMethodA()
    {

    }

    public void refineMethodB()
    {

    }
}
package org.example.bridge;

public class Main {
    public static void main(String[] args)
    {
        RefileAbstraction refileAbstraction1 = new RefileAbstraction(new concreteImplementorA());
        refileAbstraction1.method1();
        refileAbstraction1.method2();
        refileAbstraction1.refineMethodA();
        refileAbstraction1.refineMethodB();

        RefileAbstraction refileAbstraction2 = new RefileAbstraction(new concreteImplementorB());
        refileAbstraction2.method1();
        refileAbstraction2.method2();
        refileAbstraction2.refineMethodA();
        refileAbstraction2.refineMethodB();
    }
}

Bridgeパターンは、処理を機能(Abstraction)と実装(Implementor)に分けてコーディングするパターンです。

処理を拡張する場合はImplementorを継承してconcreteImplementorクラスを作成、機能側にそのインスタンスを渡すイメージです。

【ダイエット支援】スマホからでも利用できるようにしたい

次のステップとして、スマホから利用することによって、スマホの情報を利用できるようにしたいなぁ、って思ってたんですが、

それ以前の問題が起きていました。

トップページ。

ひどいなこれ。

データ入力・編集・削除ダイヤログも崩れていました。

これはひどい。

まずはこれを直すところかは始めようか。

【COCOS2D-X】ステータスウィンドウをクラス化

見た目は変わらないんですが。

前回のままだといろいろ扱いづらいので、左のウィンドウをクラス化しました。

そして、今回は、前回と同じように表示できるところまで作成しました。

このウィンドウをタップすると、画面中央に詳細が表示されるような動きにしたいと思っています。

//
// Created by ntaki on 2020/12/12.
//

#ifndef PROJ_ANDROID_CHARAWINDOW_H
#define PROJ_ANDROID_CHARAWINDOW_H

#include "cocos2d.h"
#include "GameStatusData/Character.h"

class CharaWindow {
private:
    cocos2d::Vec2 point;
    cocos2d::Size size;
    cocos2d::Sprite* sprite;
    cocos2d::Label* hpLabel;
    cocos2d::Label* MpLabel;
public:
    CharaWindow();
    ~CharaWindow();

    void show(cocos2d::Scene* scene);
    void hide(cocos2d::Scene* scene);
    cocos2d::Size getSpriteContentSize();
    cocos2d::Vec2 getPosition();
    cocos2d::Size getSize();
    void setPosition(cocos2d::Vec2 vector);
    void setScale(float rate);
    void setParameter(Character* chara);
    bool isTouch(cocos2d::Vec2 vector);
};


#endif //PROJ_ANDROID_CHARAWINDOW_H
//
// Created by ntaki on 2020/12/12.
//

#include "CharaWindow.h"

CharaWindow::CharaWindow()
{
    sprite = cocos2d::Sprite::create("btn02_03_s_bl.png");
    if (sprite != nullptr)
    {
        sprite->setAnchorPoint(cocos2d::Vec2(0,0));
        auto offset = sprite->getContentSize().width / 10.0;
        hpLabel = cocos2d::Label::createWithTTF("Hello World", "fonts/msgothic.ttc", 6);
        if(hpLabel != nullptr)
        {
            hpLabel->setAnchorPoint(cocos2d::Vec2(0, 1));
            hpLabel->setPosition(cocos2d::Vec2(0, sprite->getContentSize().height));
            sprite->addChild(hpLabel);
        }
        MpLabel = cocos2d::Label::createWithTTF("Hello World", "fonts/msgothic.ttc", 6);
        if(MpLabel != nullptr)
        {
            MpLabel->setAnchorPoint(cocos2d::Vec2(0, 1));
            MpLabel->setPosition(cocos2d::Vec2(0, sprite->getContentSize().height - 1 * hpLabel->getContentSize().height));
            sprite->addChild(MpLabel);
        }
    }
}

CharaWindow::~CharaWindow()
{
}

void CharaWindow::setPosition(cocos2d::Vec2 vector)
{
    if (sprite != nullptr)
    {
        point = vector;
        sprite->setPosition(point);
    }
}

void CharaWindow::setScale(float rate)
{
    if (sprite != nullptr)
    {
        sprite->setScale(rate);
        size = cocos2d::Size(sprite->getContentSize().width * rate, sprite->getContentSize().height * rate);
    }
}

void CharaWindow::show(cocos2d::Scene* scene)
{
    if (sprite != nullptr && scene != nullptr)
    {
        scene->addChild(sprite, 1);
    }
}

void CharaWindow::hide(cocos2d::Scene *scene)
{
    if (sprite != nullptr && scene != nullptr)
    {
        scene->removeChild(sprite, false);
    }
}

cocos2d::Vec2 CharaWindow::getPosition()
{
    return point;
}

cocos2d::Size CharaWindow::getSize()
{
    return size;
}

cocos2d::Size CharaWindow::getSpriteContentSize()
{
    if (sprite != nullptr)
    {
        return sprite->getContentSize();
    }
}

void CharaWindow::setParameter(Character* chara)
{
    auto hpStr = cocos2d::String();
    hpStr.appendWithFormat("HP : %d", chara->MaxHp);
    hpLabel->setString(hpStr.getCString());

    auto mpStr = cocos2d::String();
    mpStr.appendWithFormat("MP : %d", chara->MaxMp);
    MpLabel->setString(mpStr.getCString());
}

bool CharaWindow::isTouch(cocos2d::Vec2 vector)
{
    return false;
}
    CharaWindow* window[4];
    auto chara = GameStatus::GetGameData()->Charactors->begin();
    for(int i = 0; i < 4; i++) {
        window[i] = new CharaWindow();
        window[i]->setParameter(chara.operator*());
        window[i]->setScale((visibleSize.height / 4) / window[i]->getSpriteContentSize().height);
        window[i]->setPosition(Vec2(xpos + origin.x, origin.y + window[i]->getSize().height * i));
        window[i]->show(this);
        chara++;
    }

クラス化させることで、ウィンドウ表示処理がかなりスッキリしました。

このクラスの中で使用しているオブジェクトはnewで作成した物ではないので、cocos-2dx内部で適時ガベージコレクションされるでしょう。

【ダイエット支援】最新版をデプロイしました。

https://taki-lab.site:8443/

ポート番号の設定も修正したので、使用できるはず。

今後の予定。

やりたいこと。

  1. 栄養成分表を撮影して、OCRで解析して自動入力。
  2. バーコードをスキャンして自動入力
  3. スマホのGPSの記録から行動履歴や運動を分析する
  4. スマホに対応させちゃう
  5. あと、見た目の問題

4.見た目の問題は後々考える。

2.はハードル高そう。それを扱うデータベースやAPIがないと無理。

個人的には3.をやりたいかな、ダイエットには必要な機能。

いろいろ解決しなきゃ行けない問題はあるけど。

GPSのデータをどうやって取り込むか、とか。

まぁ、考えます。