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

【ALEXAスキル開発】テレビスキルを作成開始

前回で大体の仕組みがつかめてきました。

この画面に設定した内容は、スキルの呼び出し名を設定します。

上の設定では、「テレビを開いて」と言えばスキルが起動します。

次にインテント。

「テレビ」スキルに対する命令のようなもの、ですね。

ここでは、VolumeUpIntentを作成して、これに対応するセリフを設定します。

上の場合は「ボリュームを上げて」「ボリュームアップ」と言えば、このインテントが作動します。

次にコード。

const LaunchRequestHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'LaunchRequest';
    },
    handle(handlerInput) {
        const speakOutput = 'テレビをどうしますか?';

        return handlerInput.responseBuilder
            .speak(speakOutput)
            .reprompt(speakOutput)
            .getResponse();
    }
};

const VolumeUpIntentHandler = {
    canHandle(handlerInput) {
        return Alexa.getRequestType(handlerInput.requestEnvelope) === 'IntentRequest'
            && Alexa.getIntentName(handlerInput.requestEnvelope) === 'VolumeUpIntent';
    },
    handle(handlerInput) {
        const speakOutput = 'ボリュームを上げました';

        return handlerInput.responseBuilder
            .speak(speakOutput)
            //.reprompt('add a reprompt if you want to keep the session open for the user to respond')
            .getResponse();
    }
};

exports.handler = Alexa.SkillBuilders.custom()
    .addRequestHandlers(
        LaunchRequestHandler,
        VolumeUpIntentHandler,
        HelpIntentHandler,
        CancelAndStopIntentHandler,
        FallbackIntentHandler,
        SessionEndedRequestHandler,
        IntentReflectorHandler)
    .addErrorHandlers(
        ErrorHandler)
    .withCustomUserAgent('sample/hello-world/v1.2')
    .lambda();

まず上のLaunchRequestHandlerはスキル起動時の動作。

ここではスキル起動後、「テレビをどうしますか?」と応答がでます。

そして、VolumeUpIntentHandler。インテントがVolumeUpIntentならば「ボリュームを上げました」を返ります。

そして、最後のexports.handlerに追加したVolumeUpIntentHandlerを追加します。

動作はこんな感じになります。

「テレビ」を起動して「ボリュームを上げて」で設定した内容が返ってきました。

しかし、「テレビのボリュームを上げて」ではダメで、「テレビを開いてボリュームを上げて」と言わなければならないようです。

でも、これで一文でコントロールできそうです。

あとは、どうやってWebAPIを叩くか、ですね。

次回までに調べておきます。

【ダイエット支援】スマホから写真をアップロードできるようにする

さて、スマホのレイアウト崩れも直ったので、

これからやりたいことは、

・カメラで栄養成分表示を撮影してアップロードする

・それをOCRでテキストデータに変換し、数値を入力する

です。

まずは、スマホの画面にだけ、ファイルアップロードのコントロールを表示させます。

div#mobile {
    @include small {
    }
    @include middle {
        display: none;
    }
    @include big {
        display: none;
    }
}
                <div id="mobile">
                    <p>
                        <input type="file" name="file" />
                    </p>
                    <p>
                        <button>送信</button>
                    </p>
                </div>

メディアクエリの使い方覚えたから楽勝。

PCからはこうなってますが、

スマホからはこうなります。

で、ファイルを選択をタップすると、

こんな感じでカメラを起動して撮影したものをすぐにアップロードできるんですね。

とりあえず、今日やりたいことはできた。

次回は実際にOCRを組み込んでみたいと思います。

【COCOS2D-X】クエストリストの作成に着手

こんな感じでウィンドウを作成しました。

「冒険」ボタンをタップすると表示するようになっています。

これを縦に並べてクエストリストを表示させたいのですが、

データ構造はこんな感じで実装しました。

typedef struct _TextLabel {
    cocos2d::Label* label;
    cocos2d::Vec2 point;
} TextLabel;

typedef struct _QuestListMenu {
    Parts parts;
    TextLabel questName;
} QuestListMenu;

typedef struct _QuestList {
    Parts parts;
    QuestListMenu questListMenu[QUEST_NUM];
    float scaleRate;
} QuestList;

シーンの中ではnew演算子禁止なので、listは使えません。

散々悩んだのですが、とりあえず、大きめの配列を用意して使用することにします。

    if(isTouch(touch->getLocation(), &(questButton.parts)))
    {
        log("touch questButton");
        auto questList = getQuestList();
        auto questName = questList->begin();
        questListMenu.questListMenu->parts.sprite = Sprite::create("btn02_03_s_bl.png");
        questListMenu.questListMenu->parts.sprite->setAnchorPoint(Vec2(0.0, 1.0));
        questListMenu.questListMenu->parts.point = questListMenu.parts.point;
        questListMenu.questListMenu->parts.sprite->setPosition(questListMenu.questListMenu->parts.point);
        questListMenu.questListMenu->parts.size = Size(questListMenu.questListMenu->parts.sprite->getContentSize().width * questListMenu.scaleRate,
                                                       questListMenu.questListMenu->parts.sprite->getContentSize().height);
        questListMenu.questListMenu->parts.sprite->setScale(questListMenu.scaleRate, 1);
        this->addChild(questListMenu.questListMenu->parts.sprite, 5);
    }

タップ処理です。

今の段階では上の画面のようなウィンドウを作成追加するだけです。

今後は、数を増やしたり、テキストを追加したりする予定です。

デザインパターン】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);
                    }
                }
            }

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

がんばります。