🚧Unity&宴「ノベルゲーム」開発入門on2020.1.6f1
本書はとても親切なので本来詰まるところなどなさそうですが、Unityのバージョン違いにより詰まるところがあったので、ここでそれに対する試行錯誤を載せたいと思います。随時更新です。
環境
- macOS 11.5.2(本ではWindows 10)
- Unity 2020.1.6f1(本では2017.3.1f1)
- 宴 3.9.6(本では3.3.8)
- Visual Studio Code 1.62.0(本ではVisual Studio Community 2017)
- Excel 16.54(本では2016)
p61 新しい画像に「Packing Tag」を設定
Packing Tagはスプライトパッカー (古い機能) スプライトパッカー - Unity マニュアルと言う機能の一部で、描画を素早くするためにグラフィックを一つのテクスチャにまとめるためのものらしいです。
しかしこれは古い機能なので、もうPacking Tagが編集できなくなってしまっています。 Where is the "Packing tag"? - Unity Forum (このフォーラムの途中にある方法を試してももう編集できません。)
新しい機能であるスプライトアトラス スプライトアトラス - Unity マニュアルという機能を使ってテクスチャをまとめていきましょう。(宴との互換性は、知らん。)
Assets>Create>Sprite Atlasから新しい.spriteatlasファイルを作って、インスペクタのObjects for PackingにUtageJidaiHime/Textures/SpriteBGとUtageJidaiHime/Textures/SpriteUIを追加しておけば大丈夫な気がします。
このとき、SpriteAtlasの「Tight Packing」のチェックボックスからチェックを外さないと、画像が重なってしまい余計な画像が見えることがある。
p127 「プレハブ」と「インスタンス」
Select Revert Applyの3つのボタンが表示されるとあるが、実際はOpenとSelectしかない。横にoverridesのドロップダウンがある。
インスタンスを通したプレハブの編集 - Unity マニュアル
これを読むに、overridesドロップダウンのapply allを押せばいいのだろう。
補助pdf 名前の入力
そのままではinputfieldに日本語が入力できない。
inputfieldオブジェクトを選択し、inspectorからLineTypeをMultiLineNewlineにすること。
abc184_D - increment of coins
Solution Overview
This problem can be solved by Probability Dynamic Programming. We can calculate the expected times of the operation from that of when one more coin is there.
if we define dp[i][j][k] as the expected times of the operation from the situation we have (i,j,k) coins, we can find this value by:
dp[i][j][k]=(i/i+j+k)(1+dp[i+1][j][k])+(j/i+j+k)(1+dp[i+1][j+1][k])+(k/i+j+k)(1+dp[i][j][k+1])
Code
int A,B,C; double dp[105][105][105]; double dfs(int i,int j, int k){ if(dp[i][j][k]) return dp[i][j][k]; if(i==100 || j==100 || k==100)return 0.0; double ret=0.0; ret+=((double)i/(double)(i+j+k))*(1.0+dfs(i+1,j,k)); ret+=((double)j/(double)(i+j+k))*(1.0+dfs(i,j+1,k)); ret+=((double)k/(double)(i+j+k))*(1.0+dfs(i,j,k+1)); dp[i][j][k]=ret; return ret; } signed main(){ scanf("%lld %lld %lld",&A,&B,&C); printf("%.9lf\n",dfs(A,B,C)); }
テーブル
int A,B,C; double dp[105][105][105]; signed main(){ scanf("%lld %lld %lld",&A,&B,&C); for(int i=99;i>=0;i--){ for(int j=99;j>=0;j--){ for(int k=99;k>=0;k--){ dp[i][j][k]=0.0; if(i<100)dp[i][j][k]+=((double)i/(double)(i+j+k))*(1.0+dp[i+1][j][k]); if(j<100)dp[i][j][k]+=((double)j/(double)(i+j+k))*(1.0+dp[i][j+1][k]); if(k<100)dp[i][j][k]+=((double)k/(double)(i+j+k))*(1.0+dp[i][j][k+1]); } } } printf("%.9lf\n",dp[A][B][C]); }
ABC183_E Queen on Grid
Overall Solution
You can solve this problem by dynamic programming, because if the place of Queen was decided, you can determine the number of cases of the situation and that of visiting goal from that place, uniquely. You can memorize the number of cases when Queen is in (i,j) as dp[i][j], and do the transition like:
dp[i][j]=dp[i-1][j]+dp[i-2][j]+...+dp[i][j-1]+dp[i][j-2]+...+dp[i-1][j-1]+dp[i-2][j-2]+...
But the time complexity of this solution is O(HW(H+W)), so the time limit will be exceeded.
You can make time complexity lower by using cumulative sum. Define X[i][j],Y[i][j],Z[i][j] as:
X[i][j]=dp[i][j-1]+dp[i][j-2]+...
Y[i][j]=dp[i-1][j]+dp[i-2][j]+...
Z[i][j]=dp[i-1][j-1]+dp[i-2][j-2]+...
then, the transition can be represented as:
(when(i,j) is '.')
dp[i][j]=X[i][j]+Y[i][j]+Z[i][j]
X[i][j]=X[i][j-1]+dp[i][j-1]
Y[i][j]=Y[i-1][j]+dp[i-1][j]
Z[i][j]=Z[i-1][j-1]+dp[i-1][j-1]
(when(i,j) is '#')
dp[i][j]=X[i][j]+Y[i][j]+Z[i][j]
X[i][j]=0
Y[i][j]=0
Z[i][j]=0
Then, the time complexity of this solution is O(HW).
Notes
Don't forget to scanf until S[i][j] is # or . (because \n is read by scanf...)
Code
#include<cstdio> #include<math.h> #include<algorithm> #include<vector> #include<queue> #include<string> #include<set> #include<cstring> #include<map> using namespace std; #define int long long int #define rep(i,n) for(int i=0;i<n;i++) #define INF 1001001001 #define LLINF 1001001001001001001 #define mp make_pair #define pb push_back #define mod 1000000007 //#define mod 998244353 int H,W; char S[2000][2000]; int X[2000][2000];//dp[i][j-1]+dp[i][j-2]+... int Y[2000][2000]; int Z[2000][2000]; int dp[2000][2000]; signed main(){ scanf("%lld %lld",&H,&W); rep(i,H){ rep(j,W){ while(S[i][j]!='.' && S[i][j]!='#'){ scanf("%c",&S[i][j]); } X[i][j]=0;Y[i][j]=0;Z[i][j]=0; dp[i][j]=0; } } //Queen is at (0,0) dp[0][0]=1; rep(y,H){ rep(x,W){ if(y==0&&x==0)continue; if(S[y][x]=='.'){ if(x>0)X[y][x]=dp[y][x-1]+X[y][x-1]; if(y>0)Y[y][x]=dp[y-1][x]+Y[y-1][x]; if(x>0&&y>0)Z[y][x]=dp[y-1][x-1]+Z[y-1][x-1]; }else{ X[y][x]=0; Y[y][x]=0; Z[y][x]=0; } dp[y][x]=X[y][x]+Y[y][x]+Z[y][x]; X[y][x]%=mod; Y[y][x]%=mod; Z[y][x]%=mod; dp[y][x]%=mod; } } //print the answer printf("%lld\n",dp[H-1][W-1]%mod); return 0; }
ABC180_E Traveling Salesman among Aerial Cities
英語の練習で英語で解説記事を書いてみたいと思いますが、いつまで続くかは知りません。英語は話しているときくらいの気持ちで書くのでとてもbroken englishです…
Solution Overview
This problem can be solved by bitDP(Dynamic programming).
It is because when we know the cities where we have visited, the minimum cost so far can be uniquely decided. Memorize the minimum cost to visit S( it is a set of cities) and be at V(it is a city) , and make transition to next cities(the size is lower than N). The time complexity of this solution is O(2N*N2).
Code
#include<cstdio> #include<math.h> #include<algorithm> #include<vector> #include<queue> #include<string> #include<set> #include<cstring> #include<map> using namespace std; #define int long long int #define rep(i,n) for(int i=0;i<n;i++) #define INF 1001001001 #define LLINF 1001001001001001001 #define mp make_pair #define pb push_back #define mod 1000000007 //#define mod 998244353 int N; int X[20],Y[20],Z[20]; int caluculate_cost(int from,int to){ return abs(X[to]-X[from])+abs(Y[to]-Y[from])+max((int)0,Z[to]-Z[from]); } int dp[1<<20][20]; signed main(){ scanf("%lld",&N); rep(i,N){ scanf("%lld %lld %lld",&X[i],&Y[i],&Z[i]); } rep(i,1<<N)rep(j,N)dp[i][j]=INF; //go to first town dp[1<<0][0]=0; //do transition rep(i,1<<N){ rep(j,N){ rep(k,N){ //transition from j to k if(i&(1<<k))continue;//check if k hasn't be visited dp[i|(1<<k)][k]=min(dp[i|(1<<k)][k],dp[i][j]+caluculate_cost(j,k)); } } } //return to first city and print answer int ans=INF; rep(i,N){ ans=min(ans,dp[(1<<N)-1][i]+caluculate_cost(i,0)); } printf("%lld\n",ans); }
pythonに2種類のswitch文を追加する
概要
本記事は東京大学工学部電子情報工学科の「大規模ソフトウェアを手探る」という実験の報告記事です。(2020年度前半組)
私達の班はpython3.10に新しい三項演算子・switch文・前置インクリメントを追加してみました。この記事ではswitch文について説明します。
概要 qiita.com
前置インクリメントkkti4216.hatenablog.com
環境
詳しいビルド方法などは概要の記事に書かれると思いますが、cpython3.10(https://github.com/python/cpython/tree/master)をcloneして、所定の方法(https://devguide.python.org/setup/)でビルドしました。
手探る準備
いじるのに使ったツール
emacs: M-x gud-gdb
grep hogehoge -r -I .
-m dis(バイトコードの表示)
python3 -m dis filename
-d(パーサーのデバッグ出力を有効にする)
python3 -d
参考にしたサイト
公式ドキュメント
24. Changing CPython’s Grammar — Python Developer's Guide
ほぼほぼこのドキュメントに従って改造することが出来ました。
dis --- Python バイトコードの逆アセンブラ — Python 3.9.0 ドキュメント
こちらも参考にしました。
pythonを改造した先人たち
山のようにあります。
CPythonに後置インクリメントを加えてみた 概要とまとめ - Qiita
Pythonにプライベート変数を実装しようと試みた話。 - Qiita
Modifying the Python language in 6 minutes | Hacker Noon
Python処理系入門 〜1 + 1 で学ぶ処理系解読の基礎〜 - yigarashi のブログ
Pythonを改造してみた unless文を追加してみた - 開拓馬の厩
Pythonを改造してみた 排他的論理のブール演算子作った - 開拓馬の厩
その他
Python/バイトコード生成を読む - Code Reading Wiki
手探る
python.gram:構文を定義する
Grammer/python.gram:L157にある、if...elif...elseの構文の定義(以下)を真似して追加します。
if_stmt[stmt_ty]: | 'if' a=named_expression ':' b=block c=elif_stmt { _Py_If(a, b, CHECK((asdl_stmt_seq*)_PyPegen_singleton_seq(p, c)), EXTRA) } | 'if' a=named_expression ':' b=block c=[else_block] { _Py_If(a, b, c, EXTRA) } elif_stmt[stmt_ty]: | 'elif' a=named_expression ':' b=block c=elif_stmt { _Py_If(a, b, CHECK(_PyPegen_singleton_seq(p, c)), EXTRA) } | 'elif' a=named_expression ':' b=block c=[else_block] { _Py_If(a, b, c, EXTRA) } else_block[asdl_stmt_seq*]: 'else' ':' b=block { b }
具体的には、 Grammer/python.gram:L82
#ifdef DOSS_SWITCH | &'switcha' switcha_stmt #endif
L168
#ifdef DOSS_SWITCH switcha_stmt[stmt_ty]: | 'switcha' a=なにか1 ':' NEWLINE INDENT b=case_block DEDENT {_Py_Switcha(a,CHECK((asdl_stmt_seq*)_PyPegen_singleton_seq(p, b)),EXTRA)} case_block[stmt_ty]: | 'case' a=なにか2 ':' b=block c=case_block {_Py_Case(a,b, CHECK(_PyPegen_singleton_seq(p, c)),EXTRA)} | 'case' a=なにか2 ':' b=block c=[default_block] {_Py_Case(a,b,c,EXTRA)} default_block[asdl_stmt_seq*]: 'defaulta' ':' b=block {b} #endif
のように追加すれば良いと推定できます。ここで、「なにか1」と「なにか2」は、何か値を返すものであるということなどいろいろなことを考慮して、それぞれdisjunctionとatomを指定しました。C言語やJAVAのswitch文のような、評価した結果が整数値でないといけないとか、case文の右は定数のみとするなどの制約はありません。(ここはもっと考えればより適切なものがあるかもしれません。)
なお、switch,defaultではなくswitcha,defaultaとしたのは、テストで用いられるpythonで書かれたコードの中にswitchやdefaultという名前の関数や変数が存在するようで、make install
をしたときにエラーになってしまうためです。
変更が出来たら、make regen-pegen
を行い、文をパースして抽象構文木を構築するプログラム(Parser/parser.c)を自動生成します。
python.asdl:関数を定義する
さきほどのpython.gramで新しい関数Py_SwitchaとPy_Caseを使ったので、それを定義します。 Parser/Python. L38
--#ifdef DOSS_SWITCH | Switcha(expr test,stmt* cases) | Case(expr test, stmt* body,stmt* orelse) --#endif
変更できたらmake regen-ast
を行いInclude/Python-ast.hとPython/Python-ast.cを自動生成します。
compile.c:関数の中身を作る(バイトコードに変換する)
この時点で、(後述のようにast.cを変更する必要はあるが)switch文を実行しても構文エラーが出ないようになっているはずです。ただし何も帰ってきません。
まず、どのようなバイトコードで実現できそうかを考えます。 -m disというコマンドをつけて実行すると、pythonの仮想マシン上で実行されているバイトコードを逆アセンブルして表示してくれるようなので、if...elif...else文やfor文でどのような動作をしているのか調べてみましょう。 これを見ると、switch文の動作もLOAD_NAME→LOAD_CONST→COMARE_OP→POP_JUMP_IF_FALSE…といったように実現できそうなことが解ります。
また、switch文の右の式を評価した結果は何度も(case文が出てくるたびに)使わなければなりませんが、これはdis --- Python バイトコードの逆アセンブラ — Python 3.9.0 ドキュメントのバイトコード命令の一覧を眺めると、DUP_TOPなどで実現できそうです。
以上のことをまとめると、だいたい以下のような実装をしたいということが想像できます。
LOAD_NAME DUP_TOP LOAD_CONST(仕様上LOAD_NAMEなどのこともある) COMPARE_OP POP_JUMP_IF_FALSE (処理) JUMP_ABSOLUTE DUP_TOP LOAD_CONST COMPARE_OP POP_JUMP_IF_FALSE (処理) JUMP_ABSOLUTE ...(繰り返し) POP_TOP
自然な実装になるように、switch文の仕様は以下のようにしてあります。
- switch文に入ったときに式を一回評価する
- その値とcase文の式の値を==で比較し、Trueとなった一番上のcase文の中身を実行し、switch文を抜ける
- どのcase文も実行されなかった場合defaultを実行する
あとはcompile.cの中でそれぞれのバイトコードを使っているところを検索し、それを真似して書くということを繰り返します。 以下に具体的な実装を載せておきます。
Python/compile.c L2746
#ifdef DOSS_SWITCH static int compiler_case(struct compiler *c, stmt_ty s) { basicblock *end, *next; next = compiler_new_block(c); end = compiler_new_block(c); if (next == NULL || end == NULL)return 0; assert(s->kind == Case_kind); ADDOP(c, DUP_TOP); VISIT(c,expr,s->v.Case.test); cmpop_ty op=Eq; ADDOP_COMPARE(c,op); ADDOP_JUMP(c, POP_JUMP_IF_FALSE, next); VISIT_SEQ(c, stmt, s->v.Case.body); ADDOP_JUMP(c, JUMP_ABSOLUTE, end); compiler_use_next_block(c, next); if(s->v.Case.orelse){ VISIT_SEQ(c,stmt,s->v.Case.orelse); } compiler_use_next_block(c, end); return 1; } static int compiler_switch(struct compiler *c, stmt_ty s) { basicblock *end, *next; next = compiler_new_block(c); end = compiler_new_block(c); if (next == NULL || end == NULL) { return 0; } assert(s->kind == Switcha_kind); VISIT(c,expr,s->v.Switcha.test); compiler_use_next_block(c, next); VISIT_SEQ(c,stmt,s->v.Switcha.cases); ADDOP(c, POP_TOP); compiler_use_next_block(c, end); return 1; } #endif
L3457
#ifdef DOSS_SWITCH case Switcha_kind: return compiler_switch(c, s); case Case_kind: return compiler_case(c, s); #endif
実際には以下のように動きます
ceval.c:バイトコードの動作を定義する
既存のバイトコードの羅列により実装が出来てしまったので、今回この部分は触らなくてもよかったです。(前置インクリメントを作る際はこの部分も変更する必要がありました。)
その他
シンボルテーブルの作成
python.asdlを変更したあとにmake regen-allをすると、以下のようなwarningが出ます。
Python/symtable.c: In function 'symtable_visit_stmt': Python/symtable.c:1178:5: warning: enumeration value 'Switch_kind' not handled in switch [-Wswitch] switch (s->kind) { ^~~~~~ Python/symtable.c:1178:5: warning: enumeration value 'Case_kind' not handled in switch [-Wswitch]
これはsymtable.cという「シンボルテーブル」なるものを作っているプログラムの中に、switch文とcase文に対応する動作が記されていないことに起因します。シンボルテーブルは、バイトコードを生成する前に抽象構文木から生成され、識別子のスコープを計算するのに使う表らしいです。 よくわからない点もありますが、とりあえず他の場所を真似して、以下のようにswitch文、case文から訪れるすべての行き先を記しておきます。 Python/symtable.c L1294
#ifdef DOSS_SWITCH case Switcha_kind: VISIT(st, expr, s->v.Switcha.test); VISIT_SEQ(st, stmt, s->v.Switcha.cases); break; case Case_kind: VISIT(st,expr,s->v.Case.test) VISIT_SEQ(st, stmt, s->v.Case.body); if (s->v.Case.orelse) VISIT_SEQ(st, stmt, s->v.Case.orelse); break; #endif
抽象構文木の検証
python.asdlまで変更した時点でmake
&make-install
し、switch文を実行すると、以下のようなエラーが出ます。
python3: Parser/pegen.c:1146: _PyPegen_run_parser: Assertion `PyAST_Validate(res)' failed.
これをgdbで追っていくと、Python/ast.c:L539
case Interactive_kind: res = validate_stmts(mod->v.Interactive.body);
でvalidate_stmtsの返り値が0になっていることが問題なようです。さらにこれをgdbで追うと、validate_stmtのなかでSwitcha_kindとCase_kindがないことが問題だとわかります。よって以下のように変更すればとりあえず動きます。
Python/ast.c L404(要検証)
#ifdef DOSS_SWITCH case Switcha_kind: return 1; case Case_kind: return 1; #endif
Python/ast.cが何をしているかというと、構文木が正しいかどうかを検証しているらしい。よってここは本来は以下のように記すことで構文木の検証をするのが正しいです。
Python/ast.c L404
#ifdef DOSS_SWITCH case Switcha_kind: return validate_expr(stmt->v.Switcha.test, Load) && validate_stmts(stmt->v.Switcha.cases); case Case_kind: return validate_expr(stmt->v.Case.test, Load) && validate_stmts(stmt->v.Case.body) && validate_stmts(stmt->v.Case.orelse); #endif
全変更点一覧(eqによる比較のみ)
switch
Grammer/python.gram L82
#ifdef DOSS_SWITCH | &'switcha' switcha_stmt #endif
L168
#ifdef DOSS_SWITCH switcha_stmt[stmt_ty]: | 'switcha' a=disjunction ':' NEWLINE INDENT b=case_block DEDENT {_Py_Switcha(a,CHECK((asdl_stmt_seq*)_PyPegen_singleton_seq(p, b)),EXTRA)} case_block[stmt_ty]: | 'case' a=atom ':' b=block c=case_block {_Py_Case(a,b, CHECK(_PyPegen_singleton_seq(p, c)),EXTRA)} | 'case' a=atom ':' b=block c=[default_block] {_Py_Case(a,b,c,EXTRA)} default_block[asdl_stmt_seq*]: 'defaulta' ':' b=block {b} #endif
Parser/Python. L38
--#ifdef DOSS_SWITCH | Switcha(expr test,stmt* cases) | Case(expr test, stmt* body,stmt* orelse) --#endif
Python/ast.c L404
#ifdef DOSS_SWITCH case Switcha_kind: return validate_expr(stmt->v.Switcha.test, Load) && validate_stmts(stmt->v.Switcha.cases); case Case_kind: return validate_expr(stmt->v.Case.test, Load) && validate_stmts(stmt->v.Case.body) && validate_stmts(stmt->v.Case.orelse); #endif
Python/compile.c L2746
#ifdef DOSS_SWITCH static int compiler_case(struct compiler *c, stmt_ty s) { basicblock *end, *next; next = compiler_new_block(c); end = compiler_new_block(c); if (next == NULL || end == NULL)return 0; assert(s->kind == Case_kind); ADDOP(c, DUP_TOP); VISIT(c,expr,s->v.Case.test); cmpop_ty op=Eq; ADDOP_COMPARE(c,op); ADDOP_JUMP(c, POP_JUMP_IF_FALSE, next); VISIT_SEQ(c, stmt, s->v.Case.body); ADDOP_JUMP(c, JUMP_ABSOLUTE, end); compiler_use_next_block(c, next); if(s->v.Case.orelse){ VISIT_SEQ(c,stmt,s->v.Case.orelse); } compiler_use_next_block(c, end); return 1; } static int compiler_switch(struct compiler *c, stmt_ty s) { basicblock *end, *next; next = compiler_new_block(c); end = compiler_new_block(c); if (next == NULL || end == NULL) { return 0; } assert(s->kind == Switcha_kind); VISIT(c,expr,s->v.Switcha.test); compiler_use_next_block(c, next); VISIT_SEQ(c,stmt,s->v.Switcha.cases); ADDOP(c, POP_TOP); compiler_use_next_block(c, end); return 1; } #endif
L3457
#ifdef DOSS_SWITCH case Switcha_kind: return compiler_switch(c, s); case Case_kind: return compiler_case(c, s); #endif
Python/symtable.c L1294
#ifdef DOSS_SWITCH case Switcha_kind: VISIT(st, expr, s->v.Switcha.test); VISIT_SEQ(st, stmt, s->v.Switcha.cases); break; case Case_kind: VISIT(st,expr,s->v.Case.test) VISIT_SEQ(st, stmt, s->v.Case.body); if (s->v.Case.orelse) VISIT_SEQ(st, stmt, s->v.Case.orelse); break; #endif
おまけ:is比較を行うswitchb文を追加する
TAさんに指摘されたので、==演算子で比較するswitcha文のみでなく、is演算子で比較するswitchb文を実装してみました。
実装
大まかな流れは変わりませんが、switchの右の式の評価と一緒に評価方法(0ならeq,1ならis)をスタックにプッシュし、case文が呼ばれるたびにDUP_TOP_TWOしてそれらを使うといった感じの実装にしました。
switcha文からの変更点は以下です。
Grammer/python.gram:L84
#ifdef DOSS_SWITCH | &'switcha' switcha_stmt | &'switchb' switchb_stmt #endif
L179
#ifdef DOSS_SWITCH switcha_stmt[stmt_ty]: | 'switcha' a=disjunction ':' NEWLINE INDENT b=case_block DEDENT {_Py_Switcha(a,CHECK((asdl_stmt_seq*)_PyPegen_singleton_seq(p, b)),0,EXTRA)} switchb_stmt[stmt_ty]: | 'switchb' a=disjunction ':' NEWLINE INDENT b=case_block DEDENT {_Py_Switcha(a,CHECK((asdl_stmt_seq*)_PyPegen_singleton_seq(p, b)),1,EXTRA)} case_block[stmt_ty]: | 'case' a=atom ':' b=block c=case_block {_Py_Case(a,b, CHECK(_PyPegen_singleton_seq(p, c)),EXTRA)} | 'case' a=atom ':' b=block c=[default_block] {_Py_Case(a,b,c,EXTRA)}
Python/compile.c:L2746
#ifdef DOSS_SWITCH static int compiler_case(struct compiler *c, stmt_ty s) { basicblock *start,*is,*end, *next; start = compiler_new_block(c); is = compiler_new_block(c); next = compiler_new_block(c); end = compiler_new_block(c); if (next == NULL || end == NULL)return 0; assert(s->kind == Case_kind); ADDOP(c, DUP_TOP_TWO); ADDOP_JUMP(c, POP_JUMP_IF_TRUE,is); //if top=0 cmpop_ty op=Eq; VISIT(c,expr,s->v.Case.test); ADDOP_COMPARE(c,op); ADDOP_JUMP(c, JUMP_ABSOLUTE, start); //if top=1 compiler_use_next_block(c,is); op=Is; VISIT(c,expr,s->v.Case.test); ADDOP_COMPARE(c,op); compiler_use_next_block(c,start); ADDOP_JUMP(c, POP_JUMP_IF_FALSE, next); VISIT_SEQ(c, stmt, s->v.Case.body); ADDOP_JUMP(c, JUMP_ABSOLUTE, end); compiler_use_next_block(c, next); if(s->v.Case.orelse){ VISIT_SEQ(c,stmt,s->v.Case.orelse); } compiler_use_next_block(c, end); return 1; } static int compiler_switch(struct compiler *c, stmt_ty s) { basicblock *end, *next; next = compiler_new_block(c); end = compiler_new_block(c); if (next == NULL || end == NULL) { return 0; } assert(s->kind == Switcha_kind); VISIT(c,expr,s->v.Switcha.test); ADDOP_LOAD_CONST_NEW(c, PyLong_FromLong(s->v.Switcha.compare_type)); compiler_use_next_block(c, next); VISIT_SEQ(c,stmt,s->v.Switcha.cases); ADDOP(c, POP_TOP); ADDOP(c, POP_TOP); compiler_use_next_block(c, end); return 1; } #endif
動作
上がswitcha文、下がswitchb文を使ったときの動作です。
感想
switch文の追加には試行錯誤含め全部で5時間程度しかかかっていないと思います。ドキュメントが整理されており、またプログラムの自動生成などが整備されており、それに従って変更すればよかったことがとても大きかったです。
また、バイトコードに変換して考えるととても考えやすかったので、文法や機能を追加したいときはこの方法を取ることをおすすめします。
サーバーでunityのゲーム(?)を公開する
ノベルゲームを公開できないかなあと思い,とりあえず
【C#】Unity 2Dでできるだけ簡単にアドベンチャーゲーム(ノベルゲーム)を作る その1 - Kanchiの日記
この文字が流れるだけのアプリを公開できるか試してみました
バージョン
Unity 2020.1.6f1 Personal
設定
safariの人もプレイできるようにしたいのでとりあえずWebGL2.0ではなくWebGL1.0を使いました.
- Edit->Build Settings からPlatformをWebGLに
- さらにEdit->Build Settings->Player Settings->Playerで,
- Publishing Settings->Compression FormatをDisabledに(ファイルの容量は増えてしまいますが,これをしないとこのバージョンではUncaught ReferenceError: unityFramework is not definedエラーが起こります)
- Other Settings->Graphics APIsの+ボタンを押してWebGL 1.0を追加,上に持っていく
- Other Settings->Color SpaceをLinerからGammaに(WebGL1.0では対応していないため)
- CanvasのCanvasScalerでScreenMatchModeをShrinkへ
これでbuild&runして,できたフォルダをサーバーに移動してindex.htmlを参照すれば見ることができます.
例: naomiatlibrary.com/unity_novelgame
備考:ProxyPassで/以下にアクセスするとreactのサーバ(ポートフォリオ)に飛ばすようになっていたので,httpd.confを編集して
ProxyPass /unity_novelgame/ !
を追加し除外しました.
スマホへの対応
macでsafari, Chrome, Edge, firefoxで確認したところちゃんと見ることができましたが,Androidで確認するとレイアウトが崩れていました.
とりあえずこれを参考にして,ちょっとビルドした後のindex.htmlを変更しました. Unity WebGL ブラウザの大きさに合わせる - Qiita
以下の部分を次のように変更します.
<div id="unity-container" class="unity-desktop" style="text-align: center;">
(styleの追加)
if (/iPhone|iPad|iPod|Android/i.test(navigator.userAgent)) { //container.className = "unity-mobile"; config.devicePixelRatio = window.devicePixelRatio; container.style.overflow = 'hidden'; container.style.width = window.innerWidth + 'px'; container.style.height = window.innerHeight + 'px'; canvas.style.width =Math.min(window.innerHeight*960/600,window.innerWidth)+"px"; canvas.style.height =Math.min(window.innerWidth*600/960,window.innerHeight)+"px"; } else {
(スマホなどからのアクセスの時の処理)
こうすると,スマホなどからアクセスした時に以下のようになり,とりあえずレイアウトが崩れなくなりました.(これがいいやり方なのかは知りませんが)
(Androidで検証)