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

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

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

最新ソースはこちら(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が表示されるので、必ずブックマークに保存してください。

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

【ラズパイ】【いろいろ計測モニター】Windows側からも操作しちゃう

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

最新ソースはこちら。

ラズパイ https://github.com/takishita2nd/RaspiDisplayMonitor

Windows https://github.com/takishita2nd/IroiroMonitor

Windows側からラズパイの情報を取得できるなら、

Windows側からでもラズパイを操作する事も出来ます。

なので、Windows側からスイッチ操作を行う処理を作ってみます。

Windowsにボタンを設置。

    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
            <RowDefinition/>
        </Grid.RowDefinitions>
        <Label Grid.Row="0" Grid.Column="0" Content="時刻" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10" Width="239" Height="86" FontSize="48"/>
        <Label Grid.Row="1" Grid.Column="0" Content="温度" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="10" Width="239" Height="86" FontSize="48"/>
        <Label Grid.Row="2" Grid.Column="0" Content="湿度" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="48" Margin="10" Width="239" Height="87"/>
        <Label Grid.Row="3" Grid.Column="0" Content="CPU温度" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="48" Margin="10" Width="239" Height="87"/>
        <Label Grid.Row="4" Grid.Column="0" Content="GPU温度" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="48" Margin="10" Width="239" Height="87"/>
        <Label Grid.Row="0" Grid.Column="1" Content="{Binding DateTime}"  HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="24"/>
        <Label Grid.Row="1" Grid.Column="1" Content="{Binding Temperature}"  HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="48"/>
        <Label Grid.Row="2" Grid.Column="1" Content="{Binding Humidity}" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="48"/>
        <Label Grid.Row="3" Grid.Column="1" Content="{Binding CpuTemp}" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="48"/>
        <Label Grid.Row="4" Grid.Column="1" Content="{Binding GpuTemp}" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="48"/>
        <Button Grid.Column="0" Grid.Row="5" Grid.ColumnSpan="2" Content="ボタン" Command="{Binding ButtonClickCommand}" FontSize="48" />
    </Grid>

ボタンを押すと、ラズパイ側にPOSTリクエストを送信するようにします。

今後の拡張性を考えて、コマンド番号みたいなものを送れるようにしましょうか。

    [JsonObject("CommandModel")]
    class Command
    {
        [JsonProperty("number")]
        public int number { get; set; }
    }
    public class MainWindowViewModel : BindableBase
    {
        private const int CommandSwitch = 1;

        public DelegateCommand ButtonClickCommand { get; }

        public MainWindowViewModel()
        {
            ButtonClickCommand = new DelegateCommand(async () =>
            {
                Command cmd = new Command();
                cmd.number = CommandSwitch;
                var json = JsonConvert.SerializeObject(cmd);
                try
                {
                    HttpClient client = new HttpClient();
                    var content = new StringContent(json, Encoding.UTF8);
                    await client.PostAsync("http://192.168.1.15:8000/", content);
                }
                catch (Exception ex)
                {
                }
            });

これで、ラズパイ側にPOSTリクエストを送れるようになりました。

次はラズパイ側のコードを書いていきます。

class StubHttpRequestHandler(BaseHTTPRequestHandler):
    def do_POST(self):
        content_len = int(self.headers.get('content-length'))
        requestBody = json.loads(self.rfile.read(content_len).decode('utf-8'))

        if requestBody['number'] == 1:
            lock.acquire()
            GLCD.GLCDDisplayClear()
            lock.release()
            pushButton()

        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'))

HttpRequestHandlerクラスにdo_POSTメソッドをオーバーライドします。

これを実装することで、POSTリクエストを受診して処理することが出来ます。

受信したデータがボタン操作ならば、ラズパイ側で行っているボタン操作と同じ処理をおこないます。

def pushButton():
    global mode
    mode += 1
    if mode > 4:
        mode = 1

しかし、ここで考えなければならないのは、ラズパイ側の周期処理とHTTPサーバ処理は非同期処理を行っていると言うこと。

はい、処理が競合しちゃいます。

なので、スレッド間の待ち合わせ処理を行う必要があります。

方法はいろいろあるのですが、今回は一番簡単な方法を使用します。

Lockを使用する方法です。

lock = threading.Lock()
    try:
        while True:
            lock.acquire()
            Humidity = AM2320.GetHum()
            Temperature = AM2320.GetTemp()

            if sw == True:
                GLCD.GLCDDisplayClear()
                pushButton()
                sw = False

            if mode == 1:

-中略-

            lock.release()
            time.sleep(1)
    except KeyboardInterrupt:
        GLCD.GLCDDisplayClear()
        GPIO.cleanup()

lock = threading.Lock()を定義し、同じlockで周期処理全体と、HTTPのスイッチ処理をlock/releaseで囲みました。

これで、一方がlockされている場合、もう一方はlockがreleaseされるまで処理に待ったがかかります。

これを使用すれば、ラズパイの遠隔操作も可能になります。

【ダイエット支援】【食事管理】グラフの値をパーセンテージにして、適正量かどうかを見えるようにする。

前回までの状況はこちら

前回のままだと、摂取量をそのまま数字としてグラフに表示させているだけなので、

この数字が適正量なのかを判断するために、適正値との割合をパーセンテージにして表示させたいと思います。

といっても、どれくらいが適正量なのか、というのが、体型などに左右されやすいものなので、

こちらの記事の計算式を参考にして、

体重60kg、摂取カロリー2000kcalとして計算すると、

  • タンパク質120g(体重×2g、1g=4kcal)
  • 脂質55.5g(総カロリーの25%、1g=9kcal)
  • 炭水化物255g(総カロリーからタンパク質と脂質を除いた残り、1g=4kcal)

この数字を直に与えました。

            baseData: {
                protein: 120.0,
                liqid: 55.5,
                carbo: 255.0,
                calorie: 2000.0
            },

これ、ちゃんとした条件をもとに計算する仕組みが必要だね。

                    self.datasets.push(response.data.data.protein / self.baseData.protein * 100);
                    self.datasets.push(response.data.data.liqid / self.baseData.liqid * 100);
                    self.datasets.push(response.data.data.carbo / self.baseData.carbo * 100);
                    self.datasets.push(response.data.data.calorie / self.baseData.calorie * 100);

これでグラフがパーセンテージになります。

なので、どの栄養素が足りてなくて、どの栄養素を取りすぎているかがわかります。

【ANDROID】【実質北海道一周】移動距離に応じて表示を変える

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

簡単ですが、UIは作ったので、実際に移動距離から都市名を表示させる処理を作っていきます。

class StartEnd(startCity: String, endCity: String) {
    val startCityName : String = startCity
    val endCityName : String = endCity
}
class AroundHokkaido {

    fun getCity() : StartEnd {
        var tempDistance = 0.0
        var start : String = ""
        var end : String = ""
        var loop : Boolean = false
        run {
            citylist.cityList.forEach{
                if(loop){
                    end = it.city
                    return@run
                }else{
                    tempDistance += it.distance
                    if(totalDistance < tempDistance){
                        start = it.city
                        loop = true
                    }
                }
            }
        }
        return StartEnd(start, end)
    }

Jsonで作成した都市と距離のデータから、移動距離と都市間距離を比較しながら加算していき、都市間距離合計が移動距離を超えたときにそのときの都市を返します。

forEach()を使っているのですが、forEachは処理じゃなくて関数なので、途中で処理を抜けるには、breakではなくreturnになります。

それをrunというラベルを指定してreturnすることで、run{}を抜けるところまでジャンプできます。

ややこしい。

でも大分Kotlinにも慣れてきた気がする。

class MainActivity : AppCompatActivity() {

    private fun updateCitydistance(startEnd : StartEnd){
        startCity.text = startEnd.startCityName
        endCity.text = startEnd.endCityName
    }

kotlinはUIにアクセスするの楽だからいいね。

GPSって、思った以上にいい加減、というか、誤差が大きくて、動かしていなくても、1mぐらい移動したものと見なされるみたいです。

後々対処しないとだけど、今は、移動するのがめんどくさいので、このままにしておきます。

確認用に、Jsonを修正。

うん、きちんと変わりましたね。

修正しなきゃいけないところはあるけど、とりあえずはこれでいいか。