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

デザインパターン】Facadeパターン

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

package org.example.facade;

public class Class1 {
    public void method1()
    {

    }
}
package org.example.facade;

public class Class2 {
    public void method2()
    {

    }
}
package org.example.facade;

public class Facade {
    Class1 class1;
    Class2 class2;

    public Facade()
    {
        class1 = new Class1();
        class2 = new Class2();
    }
    public void function()
    {
        class1.method1();
        class2.method2();
    }
}
package org.example.facade;

public class Main {
    public static void main(String[] args)
    {
        Facade facade = new Facade();
        facade.function();
    }
}

Facadeパターンは、様々な機能を持つクラスの処理を、Facadeクラスの窓口に一本化するパターンです。

よく利用する一連の処理をFacadeのメソッドとして定義することで、扱いが簡単になります。

もしかしたら、デザインパターンを意識しなくてもやっているかもしれない。

【Alexaスキル開発】チュートリアルを始める

さて、最終目標はAlexaとの連携です。

なので、Alexaスキルの開発に着手していこうと思います。

しかしながら、スキル開発は全くの初めてなので、チュートリアルから始めていこうと思います。

こちらの記事に従って進めていきました。

https://developer.amazon.com/ja/blogs/alexa/post/31c9fd71-f34f-49fc-901f-d74f4f20e28d/alexatraining-firstskill

まぁ、初めはこんなもんでしょ。

次回以降もチュートリアルを進めていきます。

【ダイエット支援】スマホ画面でのダイアログ表示崩れを修正

前回の修正で

メディアクエリを使用すればレスポンシブデザインのコーディングができるよ、ということがわかりました。

では、これをsassにも適用させたいのですが、

sassに記入するには、ミックスインというものを使用するのが一般的なようです。

具体的には、こんな感じ。

@mixin small {
    @media (max-width: (479px)) {
        @content;
    }
}

@mixin middle {
    @media (min-width: (480px) and (max-width:959px)) {
        @content;
    }
}

@mixin big {
    @media (min-width: (960px)) {
        @content;
    }
}

@mixin XXでメディアクエリの定義に名前をつけ、この定義をsassの先頭あたりに記載します。

今回は大中小と記載しましたが、いいように名前を指定できます。

で、これをsassのスタイル定義に使用します。

    #content {
        z-index: 2;
        @include small {
            width: 90%;
        }
        @include middle {
            width: 50%;
        }
        @include big {
            width: 30%;
        }

こうすることでsassにもメディアクエリが適用できます。

いい感じになりました。

【COCOS2D-X】メニューボタンも作り直す

まずは、キャラクター詳細ウィンドウに表示パラメータを追加しました。

さらに、右側のメニューボタンをキャラウィンドウと同じように構造体を使って配置します。

今後、タッチ判定処理を追加することになるので。

class HomeScene : public cocos2d::Scene
{
private:
    CharaWindow window[4];
    CharaDetailWindow detailWindow;
    MenuButton homeButton;
    MenuButton charaButton;
    MenuButton equipButton;
    MenuButton questButton;
    homeButton.sprite = Sprite::create("btnHome.png");
    if(homeButton.sprite != nullptr)
    {
        homeButton.sprite->setAnchorPoint(Vec2(1.0,1.0));
        homeButton.point = Vec2(sprite->getPosition().x + sprite->getContentSize().width * scaleRate / 2 + origin.x, visibleSize.height + origin.y);
        homeButton.sprite->setPosition(homeButton.point);
        homeButton.size = Size(homeButton.sprite->getContentSize().width * buttonScale, homeButton.sprite->getContentSize().height * buttonScale);
        homeButton.sprite->setScale(buttonScale);
        buttonBase = visibleSize.height - homeButton.size.height;
        this->addChild(homeButton.sprite, 1);
    }

    charaButton.sprite = Sprite::create("btnChara.png");
    if (charaButton.sprite != nullptr)
    {
        charaButton.sprite->setAnchorPoint(Vec2(1.0,1.0));
        charaButton.point = Vec2(sprite->getPosition().x + sprite->getContentSize().width * scaleRate / 2 + origin.x, buttonBase + origin.y);
        charaButton.sprite->setPosition(charaButton.point);
        charaButton.size = Size(charaButton.sprite->getContentSize().width * buttonScale, charaButton.sprite->getContentSize().height * buttonScale);
        charaButton.sprite->setScale(buttonScale);
        buttonBase -= charaButton.size.height;
        this->addChild(charaButton.sprite, 1);
    }

    equipButton.sprite = Sprite::create("btnEquip.png");
    if (equipButton.sprite != nullptr)
    {
        equipButton.sprite->setAnchorPoint(Vec2(1.0,1.0));
        equipButton.point = Vec2(sprite->getPosition().x + sprite->getContentSize().width * scaleRate / 2 + origin.x, buttonBase + origin.y);
        equipButton.sprite->setPosition(equipButton.point);
        equipButton.size = Size(equipButton.sprite->getContentSize().width * buttonScale, equipButton.sprite->getContentSize().height * buttonScale);
        equipButton.sprite->setScale(buttonScale);
        buttonBase -= equipButton.size.height;
        this->addChild(equipButton.sprite, 1);
    }

    questButton.sprite = Sprite::create("btnQuest.png");
    if (questButton.sprite != nullptr)
    {
        questButton.sprite->setAnchorPoint(Vec2(1.0,1.0));
        auto questButtonScale = buttonBase / questButton.sprite->getContentSize().height;
        questButton.point = Vec2(sprite->getPosition().x + sprite->getContentSize().width * scaleRate / 2 + origin.x,
                                 buttonBase + origin.y);
        questButton.sprite->setPosition(questButton.point);
        questButton.size = Size(questButton.sprite->getContentSize().width * questButtonScale, questButton.sprite->getContentSize().height * questButtonScale);
        questButton.sprite->setScale(questButtonScale);
        this->addChild(questButton.sprite, 1);
    }

さて、次はタッチ判定でシーン切り替えと、クエストシーンの作成を行って行きます。

デザインパターン】Decoratorパターン

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

package org.example.decorator;

public abstract class Component {
    public abstract void method1();
    public abstract void method2();
}
package org.example.decorator;

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

    }

    @Override
    public void method2() {

    }
}
package org.example.decorator;

public abstract class Decorator extends Component {
    protected Component component;

    protected Decorator(Component component) {
        super();
        this.component = component;
    }
}
package org.example.decorator;

public class ConcreteDecorator extends Decorator {
    protected ConcreteDecorator(Component component) {
        super(component);
    }

    @Override
    public void method1() {

    }

    @Override
    public void method2() {

    }
}
package org.example.decorator;

public class Main {
    public static void main(String[] args)
    {
        Component component = new ConcreteComponent();
        Component instance = new ConcreteDecorator(component);
        instance.method1();
        instance.method2();

        Component component1 = new ConcreteDecorator(new ConcreteDecorator(component));
        component1.method1();
        component1.method2();
    }
}

このパターンでは、中身(ConcreteComponent)と装飾(ConcreteDecorator)が存在し、Componentという共通のインターフェースを持っています。

機能を追加する場合はDecoreterを継承したクラスを作成し、機能を拡張することができます。

ConcreteDecoratorのコンストラクタにはConcreteComponentを渡すこともできますし、すでに作成したConcreteDecoratorを渡すこともでき、それによって再帰的な処理をすることもできます。

しかし、クラスの関係が複雑になったりするので、クラス図による整理が必要になりますね。

【北海道大戦2021】マップの配置

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

jsonファイルのデータを元に、都市を画面に配置していきます。

            // マップの配置
            foreach (var c in gameData.Battle.GetAliveCityList())
            {
                var maps = c.GetMaps();
                foreach (var m in maps)
                {
                    m.AddLayer(layer);
                }
            }

ここは前回と変わらず。

ただ、表示する■のサイズや座標の計算処理は変えています。

そして、その都市同士のリンクを示す選も描画していきます。

            // リンクの描画
            for (int i = 1; i <= gameData.MapData.citydata.Length; i++)
            {
                var m = Singleton.FieldMap.GetMap(i);
                foreach (var linkedMap in m.GetLinkdMap())
                {
                    if(m.Id < linkedMap.Id)
                    {
                        var geometryObject = new asd.GeometryObject2D();
                        geometryObject.Color = new asd.Color(0, 0, 255);
                        geometryObject.DrawingPriority = 5;
                        var linkLine = new asd.LineShape();
                        linkLine.StartingPosition = new asd.Vector2DF(m.CenterX, m.CenterY);
                        linkLine.EndingPosition = new asd.Vector2DF(linkedMap.CenterX, linkedMap.CenterY);
                        linkLine.Thickness = 2;
                        geometryObject.Shape = linkLine;
                        layer.AddObject(geometryObject);
                    }
                }
            }

さて、後はデータを入力していくだけの力仕事だ。

がんばります。

【ラズパイ】WebAPIで赤外線信号を送信する

結局は、ほとんどの信号をmode2コマンドでトレースして、赤外線信号データを登録しました。

https://github.com/takishita2nd/tv_ir/blob/master/tv.conf

全てきちんと動作することを確認しています。

これをWebAPIで実行できるように、簡易httpサーバをpythonで作成します。

すでにやってきたことなので、サクッと作成。

import json
import time
import threading
import subprocess

from http.server import BaseHTTPRequestHandler
from http.server import HTTPServer
from http import HTTPStatus
from typing import TypeVar
from urllib.parse import urlparse

PORT = 8000

def __main__():
    thread = threading.Thread(target=httpServe)
    thread.start()
    
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        return

def httpServe():
    handler = StubHttpRequestHandler
    httpd = HTTPServer(('',PORT),handler)
    httpd.serve_forever()

class StubHttpRequestHandler(BaseHTTPRequestHandler):
    server_version = "HTTP Stub/0.1"
    thread = None
    aviFilename = ""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

    def do_POST(self):
        content_len = int(self.headers.get('content-length'))
        requestBody = json.loads(self.rfile.read(content_len).decode('utf-8'))
        command = requestBody['contents']['command']
        args = ['irsend', 'SEND_ONCE', 'tv', command]
        res = ""
        try:
            res = subprocess.run(args, stdout=subprocess.PIPE)
        except:
            ""

        response = { 'status' : 200 }
        self.send_response(200)
        self.send_header('Content-type', 'application/json')
        self.end_headers()
        responseBody = json.dumps(response)

        self.wfile.write(responseBody.encode('utf-8'))

__main__()

これに対して、PostmanというWebAPIを送信するツールを使って、リクエストを送ります。

これで動きました。

【ダイエット支援】スタイルの修正ってめんどくさいね・・・

TechCommitでレイアウト崩れの相談をしたところ、

            .full-height {
                height: 100vh;
            }

これがあると、高い確率でスマホのレイアウトが崩れるらしい(と言う認識)

なので、削除。

.flex-center {
    align-items: center;
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
}

display: flex;とflex-wrap: wrap;はセットで考えた方が良いらしい(という認識)

flex-wrap: wrap;を追加。

@media screen and (max-width:479px) {
    .title {
        font-size: 34px;
    }
    #whatsthis {
        margin-left: 10%;
        font-size: 10px;
    }
}

@media screen and (min-width:480px) and (max-width:959px){
    .title {
        font-size: 44px;
    }
    #whatsthis {
        font-size: 15px;
    }
}

@media screen and (min-width:960px) {
    .title {
        font-size: 84px;
    }
    #whatsthis {
        font-size: 20px;
    }
}

あとはメディアクエリを使って、ディスプレイのサイズに応じて、スタイルを切り替える、と言う手法を使うらしい。

レスポンシブデザインでは常套手段みたい。

ということで、ここまできれいになりました。

頑張った、俺。

【COCOS2D-X】結局キャラクターウインドウを構造体で作り直す。

結局クラスは使えない、ということで、

やっぱりここは構造体にするしか無いだろう、と思いまして、

めっちゃ書き換えました。

詳細はgitHubを見てくれ。

https://github.com/takishita2nd/cocos2d-x_sample


typedef struct _ParameterLabel {
    cocos2d::Label* label;
    cocos2d::Vec2 point;
} ParameterLabel;

typedef struct _CharaWindow {
    cocos2d::Sprite* sprite;
    cocos2d::Vec2 point;
    cocos2d::Size size;
    ParameterLabel HPLabel;
    ParameterLabel MPLabel;
} CharaWindow;

ウインドウは構造体でまとめました。

これをシーンクラスのprivateで保持し、画面を作成していきます。

    auto chara = gameStatus.Charactors->begin();
    for(int i = 0; i < 4; i++) {
        window[i].sprite = cocos2d::Sprite::create("btn02_03_s_bl.png");
        if (window[i].sprite != nullptr)
        {
            auto scaleRate = (visibleSize.height / 4) / window[i].sprite->getContentSize().height;
            window[i].point = Vec2(xpos + origin.x,
                                   origin.y + window[i].sprite->getContentSize().height * scaleRate * i);
            window[i].size = Size(window[i].sprite->getContentSize().width * scaleRate,
                                  window[i].sprite->getContentSize().height * scaleRate);
            window[i].sprite->setAnchorPoint(Vec2(0,0));
            window[i].sprite->setScale(scaleRate);
            window[i].sprite->setPosition(window[i].point);
            this->addChild(window[i].sprite);

            window[i].HPLabel.label = Label::createWithTTF("", "fonts/msgothic.ttc", 10);
            auto str = String();
            str.appendWithFormat("HP:%d/%d", chara.operator*()->Hp, chara.operator*()->MaxHp);
            window[i].HPLabel.label->setString(str.getCString());
            window[i].HPLabel.label->setAnchorPoint(Vec2(0.0, 1.0));
            window[i].HPLabel.point = Vec2(window[i].point.x + window[i].point.x / 10.0,
                                           window[i].point.y + window[i].size.height - window[i].size.height / 20.0);
            window[i].HPLabel.label->setPosition(window[i].HPLabel.point);
            this->addChild(window[i].HPLabel.label, 10);

            window[i].MPLabel.label = Label::createWithTTF("", "fonts/msgothic.ttc", 10);
            str = String();
            str.appendWithFormat("MP:%d/%d", chara.operator*()->Mp, chara.operator*()->MaxMp);
            window[i].MPLabel.label->setString(str.getCString());
            window[i].MPLabel.label->setAnchorPoint(Vec2(0.0, 1.0));
            window[i].MPLabel.point = Vec2(window[i].point.x + window[i].point.x / 10.0,
                    window[i].HPLabel.label->getPosition().y - window[i].HPLabel.label->getContentSize().height - window[i].size.height / 20.0);
            window[i].MPLabel.label->setPosition(window[i].MPLabel.point);
            this->addChild(window[i].MPLabel.label, 10);
        }
        chara++;
    }

もはや、何やってるか解らないレベル。

緻密にパーツを配置する座標とか計算しています。

で、左のウインドウをタップすると、中央に詳細画面が表示される仕組みにしています。

    if(detailWindow.isShow)
    {
        this->removeChild(detailWindow.sprite);
        this->removeChild(detailWindow.HPLabel.label);
        this->removeChild(detailWindow.MPLabel.label);
    }

    for(int i = 0; i < 4; i++)
    {
        if(isTouch(touch->getLocation(), &window[i]))
        {
            detailWindow.isShow = true;
            if(detailWindow.showIndex == i)
            {
                detailWindow.showIndex = -1;
                break;
            }
            detailWindow.showIndex = i;
            detailWindow.sprite = Sprite::create("btn02_03_s_bl.png");
            detailWindow.sprite->setAnchorPoint(Vec2(0.0, 0.0));
            detailWindow.sprite->setPosition(detailWindow.point);
            detailWindow.size = Size(window[i].sprite->getContentSize().width * detailWindow.scaleRate,
                                     window[i].sprite->getContentSize().height * detailWindow.scaleRate);
            detailWindow.sprite->setScale(detailWindow.scaleRate);
            this->addChild(detailWindow.sprite, 1);

            auto character = gameStatus.Charactors->begin();
            for(int j = 0; j < i; j++)
            {
                character++;
            }
            detailWindow.HPLabel.label = Label::createWithTTF("", "fonts/msgothic.ttc", 10);
            auto str = String();
            str.appendWithFormat("HP:%d/%d", character.operator*()->Hp, character.operator*()->MaxHp);
            detailWindow.HPLabel.label->setString(str.getCString());
            detailWindow.HPLabel.label->setAnchorPoint(Vec2(0.0, 0.0));
            detailWindow.HPLabel.point = Vec2(detailWindow.point.x + detailWindow.point.x / 10.0,
                                              detailWindow.point.y + detailWindow.size.height - detailWindow.size.height / 10.0);
            detailWindow.HPLabel.label->setPosition(detailWindow.HPLabel.point);
            this->addChild(detailWindow.HPLabel.label, 10);

            detailWindow.MPLabel.label = Label::createWithTTF("", "fonts/msgothic.ttc", 10);
            str = String();
            str.appendWithFormat("MP:%d/%d", character.operator*()->Mp, character.operator*()->MaxMp);
            detailWindow.MPLabel.label->setString(str.getCString());
            detailWindow.MPLabel.label->setAnchorPoint(Vec2(0.0, 0.0));
            detailWindow.MPLabel.point = Vec2(detailWindow.point.x + detailWindow.point.x / 10.0,
              detailWindow.point.y + detailWindow.size.height - detailWindow.HPLabel.label->getContentSize().height - detailWindow.size.height / 10.0);
            detailWindow.MPLabel.label->setPosition(detailWindow.MPLabel.point);
            this->addChild(detailWindow.MPLabel.label, 10);
        }
    }

疲れた・・・