こちらの話の続き。
ついでだからWindowsで動かすところまでやりました。
Android端末が入らない分、デバッグが楽になるかもしれないので。
必要なのはVisual Studio 2017。
2019ではダメです。
ツールの機能取得で、以下の機能をインストールします。
たぶん、必要なのはこれだけだと思う。
cocos run -p win32
で動きました。
Visual Studio 2017のRUNでも動くよ。
一応、プロジェクトを作成して、Hello Worldが出るまでやりました。
手順などは、Qiitaにまとめましたが、
https://qiita.com/takishita2nd/items/0b54af9860f54c65fd24
実際はもっと手こずったので、いろいろと愚痴を書きたい。
まず、Python2.7で無ければセットアップからプロジェクト作成まで動かないのだが、
Python3が動いているのは分かっているのにPython3の本体がどこにあるか分からない、という自体に。
そもそもWindowsにインストールした覚えがない。
ググってプログラムの本体を調べるコマンドを探しまして、
where python
って打てば良いんですけど、
これコマンドプロンプロのコマンドね。
最近はPowerShellをよく使うから。
C:\Users\[ユーザー名]\AppData\Local\Microsoft\WindowsApps\python.exe
にありました。
分かるか。
展開したPython2.7を展開し、環境変数のPATHの設定で、上のフォルダの記載がある場所の上にPython2.7のPATHを書かなければならない。
PATHの検索順をPython2.7→Python3に変えるんですね。
めんどくせぇ。
今時Python2.xなんて使うやついないよ。(たぶん)
もう設定元に戻したわ。
プロジェクト作るときだけ変えれば良い。
そして、もう一つ、Cocoa2d-xってWindowsでも動かすことができるんですが、
いわゆるマルチプラットフォームになっていて、C++の共通コードだけ記述すれば、他のOSでも動かすことができる。
しかし、Windowsで動かす場合は
Visual Studio 2017が必要。
2019ではダメらしい。
めんどくさい。
(一応動かしたけど、後でまとめるわ)
でも、動いたので、あとはガリガリC++のコードを書いていけば。
IDE何使えば良いんだ?
以前取り上げたopenTKのやつ。
これを使ってSTLファイルを表示する、というのをやってみたいと思います。
使用するSTLファイルは、こちらからダウンロードしました。
https://www.3dagogo.com/creativetools/designs/3DBenchy
ただしく処理できれば、こんな風に表示されるはずです。
STLファイルの読み込みはこちらのサイトを参考にしました。
https://codingsquare.net/cs/stlfile/#toc11
STLのデータは法線ベクトルと、三角形を構成する頂点の座標を示す3つのベクトルで構成されています。
法線ベクトルは三角形の面の表側を向いている方向を示すベクトルです。
public bool ReadBinary(string filePath)
{
// filePath が null か、ファイルが存在しない場合はエラーとする
if (filePath == null || File.Exists(filePath) == false)
return false;
try
{
// バイナリファイルの読み込み
using (var reader = new BinaryReader(new FileStream(filePath, FileMode.Open, FileAccess.Read)))
{
// ヘッダ読み込み
Header = reader.ReadBytes(HeaderLength);
// ファセットの枚数読み込み
uint size = reader.ReadUInt32();
// ファイルの残りのバイト数
long rest = reader.BaseStream.Length - reader.BaseStream.Position;
// ファセット1枚分のバイト数
const int FacetLength = 50;
// ファイルの残りのバイト数が、求められるファセットの枚数分のバイト数より少なければエラー
if (rest < FacetLength * size)
return false;
// 全ファセット読み込み
Facets = new Facet[size];
for (int i = 0; i < size; ++i)
{
// ファセット1個分のバイト配列読み込み
byte[] bytes = reader.ReadBytes(FacetLength);
// ファセットデータ生成と配列への格納
int index = 0;
const int offset = sizeof(float);
Facets[i] = new Facet(
new Vertex(
BitConverter.ToSingle(bytes, index),
BitConverter.ToSingle(bytes, index += offset),
BitConverter.ToSingle(bytes, index += offset)),
new Vertex(
BitConverter.ToSingle(bytes, index += offset),
BitConverter.ToSingle(bytes, index += offset),
BitConverter.ToSingle(bytes, index += offset)),
new Vertex(
BitConverter.ToSingle(bytes, index += offset),
BitConverter.ToSingle(bytes, index += offset),
BitConverter.ToSingle(bytes, index += offset)),
new Vertex(
BitConverter.ToSingle(bytes, index += offset),
BitConverter.ToSingle(bytes, index += offset),
BitConverter.ToSingle(bytes, index += offset))
);
}
}
}
catch (Exception)
{
return false;
}
return true;
}
これでバイナリのSTLデータを読み込み、描画させます。
STLFile stlFile = new STLFile();
public Game() : base(800, 600, GraphicsMode.Default, "0-3:GameWindow")
{
stlFile.ReadBinary("3DBenchy.stl");
}
//画面描画で実行される。
protected override void OnRenderFrame(FrameEventArgs e)
{
base.OnRenderFrame(e);
GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
GL.MatrixMode(MatrixMode.Modelview);
Matrix4 modelview = Matrix4.LookAt(Vector3.Zero, Vector3.UnitZ, Vector3.UnitY);
GL.LoadMatrix(ref modelview);
int count = 0;
foreach(var f in stlFile.Facets)
{
count++;
GL.Begin(BeginMode.Triangles);
GL.Color4(Color4.White);
GL.Normal3(f.Normal.X, f.Normal.Y, f.Normal.Z);
GL.Vertex3(f.Vertex1.X / 30, f.Vertex1.Y / 30, f.Vertex1.Z / 30);
GL.Vertex3(f.Vertex2.X / 30, f.Vertex2.Y / 30, f.Vertex2.Z / 30);
GL.Vertex3(f.Vertex3.X / 30, f.Vertex3.Y / 30, f.Vertex3.Z / 30);
GL.End();
}
SwapBuffers();
}
そしてライトの設定も加えます。
こちらのサイトを参考にしました。
https://ameblo.jp/nishi-u6fa4/entry-10864018960.html
//ウィンドウのサイズが変更された場合に実行される。
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
GL.Viewport(ClientRectangle.X, ClientRectangle.Y, ClientRectangle.Width, ClientRectangle.Height);
GL.MatrixMode(MatrixMode.Projection);
Matrix4 projection = Matrix4.CreatePerspectiveFieldOfView((float)Math.PI / 4, (float)Width / (float)Height, 1.0f, 64.0f);
GL.LoadMatrix(ref projection);
GL.MatrixMode(MatrixMode.Modelview);
Matrix4 look = Matrix4.LookAt(3.0f * Vector3.One, Vector3.Zero, Vector3.UnitY);
GL.LoadMatrix(ref look);
GL.Enable(EnableCap.Lighting);
float[] position = new float[] { 1.0f, 2.0f, 3.0f, 0.0f };
GL.Light(LightName.Light0, LightParameter.Position, position);
GL.Enable(EnableCap.Light0);
}
実行結果はこちら。
なんだこりゃ。
たぶん、データは間違ってないと思うんですよ。
問題は視点なのかな、と思います。
視点を変えるとか、変更できるようにすればちゃんと表示させることができるかもしれません。
もうちょっと勉強します。
まぁ、いまのままでもそこそこ楽しめるのですが、
ゲーム性を高めるために、いろいろ実装していこうかと思います。
まずは、今のバトルは完全乱数で発生させた値でのバトルなので、
ゲーム性を高めるために、じゃんけんバトルのようなものにしようかと思います。
そうなると必要なのがAltseedのシーン切り替え機能ですかね。
このシーン切り替え機能を使用するのに、どれだけ回収が必要なのかも考えないと行けません。
その検証を次回やりましょう。
あと、今は防御側勝利時、特に何もメリットがないので、防御側勝利時に1ターン戦力ボーナス・ペナルティを付与しようかなと思っています。
たぶん、そうすることでゲーム性が向上すると思うんですよね。
よし、がんばります。
あ、あと、Altseed2というのがリリースされたみたいですね。
https://altseed.github.io/index.html
とりあえず、少し改造しました。
調べてみると、GPU温度も測定できるみたいなので、それも入れてみました。
ラズパイのGPU温度を確認するコマンドは、
sudo /opt/vc/bin/vcgencmd measure_temp
sudoなので、pythonプログラム実行時もsudoで管理者権限で実行しなければなりませんが、
サービスで実行させているので、基本的に管理者権限で動作しているので、問題無いでしょう。(実際、動いた。)
時刻というのは現在時刻じゃなくて、最後に測定した時刻です。
HTTPでラズパイからデータを取得していますが、間隔を短くすると、ラズパイZeroでもCPU温度が上がってしまうので、少し間隔を空けています。(1分ぐらい)
次どうしようか。
新しい部品があればネタに出来そうだけど、今はお金が無いので。
グラフ化させてみる?
前回までの状況はこちら
最新ソースはこちら(gitHub)
https://github.com/takishita2nd/diet-mng
ダッシュボードに表示するグラフを作成していきます。
どんあグラフにしようかというと、
こんな感じのレーダーグラフです。
一日分の摂取したタンパク質、脂質、炭水化物などの情報を取得するAPIが必要になります。
まずは、それを作ります。
app/Repository/EatingManagementRepository.php
/**
* 一日あたりのデータを取得する
*/
public function getDaily($user, $date)
{
$eatings = $user->EatingManagements()
->where(DB::raw('date_format(date, "%Y-%m-%d")'), $date)
->get();
$retDatas = [];
for($j = 2; $j < count($this->paramNames); $j++) {
$retDatas[$this->paramNames[$j]] = 0;
}
foreach($eatings as $eating) {
for($j = 2; $j < count($this->paramNames); $j++) {
$retDatas[$this->paramNames[$j]] += $eating->{$this->paramNames[$j]};
}
}
return $retDatas;
}
$dateに取得する日付が入ります。
その日付に一致するデータをすべて取得し、それを栄養素毎に集計します。
これをAPIで取得するようにします。
app/Http/Controllers/Eating/ApiController.php
/**
* グラフ用データを取得する
*/
public function graph(Request $request)
{
return response()->json(['data' => $this->eatingManagement->getDaily(Auth::user(), $request->contents['date'])]);
}
routes/web.php
Route::post('api/eating/graph', 'Eating\ApiController@graph');
これをダッシュボードからデータを取得し、グラフにします。
resources/assets/js/components/Eating/EatingDashboardComponent.vue
param: {},
contents: {
date: "",
},
label: ['タンパク質', '脂質', '炭水化物', 'カロリー'],
datasets: [],
sub: 0,
};
},
created: function() {
this.todayDate = this.getDate(this.sub);
},
mounted: function() {
this.graphUpdate();
},
methods: {
getDate: function(sub) {
var today = new Date();
return today.getFullYear() + "-" + ('00'+(today.getMonth() + 1)).slice( -2 ) + "-" + ('00'+(today.getDate() + sub)).slice(-2);
},
onClickNext: function() {
this.sub++;
this.todayDate = this.getDate(this.sub);
this.graphUpdate();
},
onClickPrev: function() {
this.sub--;
this.todayDate = this.getDate(this.sub);
this.graphUpdate();
},
onClickInput: function() {
this.showInputDialogContent = true;
},
invokeUpdateList: function() {
this.graphUpdate();
},
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(response.data.data.protein);
self.datasets.push(response.data.data.liqid);
self.datasets.push(response.data.data.carbo);
self.datasets.push(response.data.data.calorie);
var myChart = new Chart(ctx, {
type: 'radar',
data: {
labels: self.label,
datasets: [{
label: self.todayDate,
data: self.datasets,
backgroundColor: 'RGBA(225,95,150, 0.5)',
borderColor: 'RGBA(225,95,150, 1)',
borderWidth: 1,
pointBackgroundColor: 'RGB(46,106,177)'
}]
},
options: {
title: {
display: true,
text: '摂取栄養素'
},
scale:{
ticks:{
suggestedMin: 0,
suggestedMax: 100,
}
}
}
});
} else {
var myChart = new Chart(ctx, {
type: 'radar',
data: {
labels: self.label,
datasets: [
]
},
});
}
}).catch(function(error){
});
}
デフォルトはアクセスした当日を表示し、next、prevクリックでそれぞれ次の日、前の日に切り替えることができるようにします。
getDate()で取得する日付の文字列を取得し、これをAPIのパラメータとします。
取得したデータをグラフのdatasets[]に設定すれば、グラフが表示されます。
次回はこのグラフをもっと見やすいようにカスタマイズしていきます。
さて、
このUIを作成していきます。
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/startCity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="36sp"
app:layout_constraintBottom_toTopOf="@+id/endCity"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.425"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.528" />
<TextView
android:id="@+id/endCity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="36sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.425"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.591" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="↓"
android:textSize="36sp"
app:layout_constraintBottom_toTopOf="@+id/endCity"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.402"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/startCity"
app:layout_constraintVertical_bias="0.525" />
<TextView
android:id="@+id/distanceSection"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="280dp"
android:textAlignment="center"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/textView3"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/distanceFromStart"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="364dp"
android:textAlignment="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/textView3" />
</androidx.constraintlayout.widget.ConstraintLayout>
レイアウトとか、細かいところよくわかっていないので、適当です。
もしかしたら、機種によってレイアウトが崩れるとかもあるかも。
次回は実際に表示する処理を作成していきます。
前回までの状況はこちら。
最新ソースはこちら(gitHub)
https://github.com/takishita2nd/HokkaidoWar
さて、現在のソースでは、ターンが飛ばされる都市がある事が分かりました。
原因はこの_citiesを一つで存在する都市と行動する都市を管理していたためです。
ここで行動済みの都市を削除すると(図の真ん中)、
図の右のように行動順が二つズレてしまうんですよね。
なので、次に行動するはずだった都市が飛ばされてしまいます。
なので、存在する都市と行動する都市を分けて管理する必要があります。
List<City> cities = null;
List<City> aliveCities = null;
必要になるのは、List<City>をコピーする処理。
このとき、Listの部分だけを複製して、中の都市オブジェクトは共有で管理します。
private List<City> copyCity(List<City> cities)
{
List<City> ret = new List<City>();
foreach(var c in cities)
{
ret.Add(c);
}
return ret;
}
こんな感じで修正してみました。
これで、順番が飛ばされることはなくなるはず。
いやー焦った焦った。
ブログの様子を見ようと思ったらこれだよ。
ERR_CERT_DATE_INVALID
というメッセージから読み取るに、
エラー、証明書、日付、不正。
ああ、証明書の日付がおかしな事になっているのね。
このサイトのSSL証明書はLet’s Encryptというツール(?)で発行しているので
ただ、自動更新する設定になっているはずだが、それがたまたま上手くいっていないのだろう。
SSHでサーバにログインし、証明書の日付を確認する。
コマンドはこれ。
$ sudo openssl x509 -in /etc/letsencrypt/live/【ドメイン名】/fullchain.pem -noout -dates
notAfterが証明書の有効期限が切れる日付です。
なるほど、8月20日午前7時11分(標準時刻)で期限が切れていたみたい。
$ sudo letsencrypt renew
で、手動による証明書更新を試みたものの、なぜかスキップされたので、
強制的に更新させました。
$ sudo letsencrypt renew --force-renewal
11月19日午前11時32分(標準時刻)に更新されました。
あ、標準時刻だから、日本時間はこれに+9時間する必要があるからね。
そして、忘れてはいけない、nginxの再起動。
$ sudo systemctl restart nginx.service
エラーは解消されました!!