|
|
|
2011/6/18(Sat) 22:57:34|NO.39714
おそらく構造体に関しては、これまで幾度も議論なり要望なり行われたものと存じますが..
例として、ゲーム等で使用される選択肢を実装する場合、アイテムには表示テキストと処理内容の二つのパラメータが必要になりますが、
これをHSPで実装する場合、おおかた以下のように組まれると思います。
screen 0, 200, 115
title "spaceで決定"
sdim select_texts, 64
dimtype select_lavels, 1
i = 0
select_texts.i = "アイテム1"
select_lavels.i = *onselect_item1
i++
select_texts.i = "アイテム2"
select_lavels.i = *onselect_item2
i++
select_texts.i = "アイテム3"
select_lavels.i = *onselect_item3
i++
select_texts.i = "アイテム4"
select_lavels.i = *onselect_item4
i++
select_sum = i
*main
color : boxf
stick key, 0
if (key && 16) : gosub select_lavels.active_select
if (key && 2) : active_select--
if (key && 8) : active_select++
active_select = limit(active_select, 0, select_sum - 1)
repeat select_sum
if (cnt == active_select) {
color 0, 0, 200
} else {
color 100, 100, 100
}
pos 10, 10 + 25 * cnt
boxf ginfo_cx, ginfo_cy, ginfo_cx + 120, ginfo_cy + 20
color 255, 255, 255
pos ginfo_cx + 20, ginfo_cy
mes select_texts.cnt
loop
wait 10
goto *main
*onselect_item1
title "item1"
return
*onselect_item2
title "item2"
return
*onselect_item3
title "item3"
return
*onselect_item4
title "item4"
return
この例では選択肢の各アイテムのインデックスに、テキスト配列とラベル配列を対応させることでパラメータを割り当てています。
パラメータ毎に配列を用意しプレフィックスを与えることで管理するという、言うまでもない一般的な方法かと存じます。
ただこの方法には変数が増え変数名も冗長になるという欠点があります。
他にも二次元配列を無理やり構造体に見立てて実装するやり方もありますが、
この場合はHSPの配列の仕様上、型の異なる値を含めることができないのと、可視的にパラメータを判断できない等の不都合からマイナーな手法かと存じます。
いずれにせよ共通して言えることは、構造体が無いなりにみなデータを工夫して管理しているという点です。
しかしながら、こういった工夫や管理はコーディングに長けた上級者にこそ向き、初心者には不便・困難なのではないかというのが私見です。
配列が一つの型に依存しないようになり、添え字に文字列(または文字列をキーのエイリアスに)指定できればいいのですが、
さすがにこれでは大きな仕様改変になるので、この先に構造体型変数なるものができれば良いなあと思っています。
仮に上記の例が以下のように書ければとても便利だと思います。
selects.i.text = "item"
selects.i.lavel = *on_select
このように、異なる型を混同でき自由に識別子を付けられる構造体は、
初級者から上級者までもが便利に活用でき、HSPのコンセプトにも背かないと思われる魅力的な概念であると思います。
というわけで、今後構造体に近似した機能が実装される予定があるなら知っておきたいし、
ないならここで改めて強く要望したい次第です。
| |
|
2011/6/19(Sun) 00:39:08|NO.39716
正直、それ位ならモジュール変数で十分対応できる気がします。以下は一例
screen 0, 200, 115
title "spaceで決定"
item = 0
#define set_item_macro(%1,%2) newmod item, m_Item \
: label = %2 \
: set_item item(length(item)-1), %1, label
set_item_macro "アイテム甲", *onselect_item甲
set_item_macro "アイテム乙", *onselect_item乙
set_item_macro "アイテム丙", *onselect_item丙
set_item_macro "アイテム丁", *onselect_item丁
#undef set_item_macro
*main
stick key, 0
if (key && 16) : cmd_item_gosub_label item(active_select)
if (key && 2) : active_select--
if (key && 8) : active_select++
active_select = limit(active_select, 0, length(item) - 1)
redraw 0
color
boxf
repeat length(item)
if (cnt == active_select) {
color 0, 0, 200
} else {
color 100, 100, 100
}
pos 10, 10 + 25 * cnt
boxf ginfo_cx, ginfo_cy, ginfo_cx + 120, ginfo_cy + 20
color 255, 255, 255
pos ginfo_cx + 20, ginfo_cy
mes get_item_name(item(cnt))
loop
redraw 1
wait 10
goto *main
*onselect_item甲
title "甲:元ネタは尻尾の付いた亀"
return
*onselect_item乙
title "乙:元ネタはジグザグ"
return
*onselect_item丙
title "丙:元ネタは脚の張り出した台"
return
*onselect_item丁
title "丁:元ネタは釘"
return
#module m_Item 名称, 処理
#modinit
名称 = ""
処理 = *未定義
return
#modfunc set_item str name, var label
名称 = name
if vartype(label) = 1 {
処理 = label
} else {
処理 = *未定義
}
return
#modfunc cmd_item_gosub_label
gosub 処理
return
#modcfunc get_item_name
return 名称
*未定義
return
#global
むしろアイテムのような、各個体の構成要素が種類によって大きく変わるデータ構造の場合、構造体よりモジュール変数の方が使い勝手が良い気がします。何故なら、モジュール変数は各要素をポインタとして扱っているようなので、各要素毎に全く違う構造のデータを保有する事ができるからです。
/*C構造体の場合*/
struct item {
char name;
int price;
int kind;
struct weapon {
int kind;
int attribute;
int power;
int weight;
} arm;
struct recover {
int scope;
int effect;
} drug;
};
/*モジュール変数の場合*/
#module m_Drug 範囲, 効力
#global
#module m_Arm 種類, 属性, 威力, 重量
#global
#module m_Item 名称, 価格, 種類, アイテムの特有効果
#global
個人的にはnewmod命令で新規に獲得した配列の番号がstat辺りに返ってくれると非常に助かります。
引数0のnewmod命令でとりあえず実体だけを作っておいて、中身の作成は#modfunc命令で作った命令に任せる事が多いので、newmod命令で作られた実体の場所を探す必要がある為です。
| |
|
2011/6/19(Sun) 01:39:42|NO.39717
>個人的にはnewmod命令で新規に獲得した配列の番号がstat辺りに返ってくれると非常に助かります。
ほんとそうですよねー;
自分は、以前この掲示板で教えてもらった
#module
#defcfunc getModuleID var In_module, local Out_ID
mref Out_ID, 1
return Out_ID
#global
#module TestModule ThisID
#modinit
ThisID = getModuleID( thismod )
return ThisID
#global
newmod ModuleVar, TestModule
mes stat
newmod ModuleVar, TestModule
mes stat
newmod ModuleVar, TestModule
mes stat
を使っていますが、
クラスや構造体を実装しないのならば、モジュール周りを強化して欲しいという願望はありますね。
HSPLetやHSPコンバーターでもモジュール変数は使えないので不便に感じています。
|
|
2011/6/19(Sun) 01:57:57|NO.39718
お。なんてタイムリー。
ちょうど関連した事をHSP開発wikiで考えてたんです。(↓コメ欄)
http://hspdev-wiki.net/?hsp3%2F%A5%E2%A5%B8%A5%E5%A1%BC%A5%EB%CA%D1%BF%F4
標準では配列取得命令ないから削除のとき困るんですよね。
なのでこんな風にやってみたんですが…こんな事してるのサンプルとかでも見たことないからちょっと不安。
#module mod_test prm1
;------------------------
; コンストラクタ
; モジュール変数に値を登録
#modinit
count++
prm1 = count * 10
mes "set:" + prm1
return
;------------------------
; デストラクタ
; モジュール変数を開放したときの処理
#modterm
mes "削除しました。"
return
;------------------------
; 内容取得
#modfunc mod_get
return prm1
;------------------------
; 条件を満たすモジュール変数を削除
#modfunc mod_del int pp
if prm1 = pp : delmod thismod
return
#global
;#################################################
;モジュール変数作成
repeat 3
newmod a, mod_test
loop
gosub *disp ;内容表示
;条件を満たすモジュール変数を削除
foreach a
mod_del a(cnt), 20 ;中身が20なのだけ消す
loop
gosub *disp ;内容表示
;追加
newmod a, mod_test
gosub *disp ;内容表示
stop
;################################################################
*disp
;内容表示
mes "-<内容確認>---------------------------------"
foreach a
mod_get a(cnt)
mes "num("+cnt+"):" + stat
loop
mes "----------------------------------------------"
return
モジュール変数で配列番号が取得できないのは使うな・気にすんなって事だと思うんですよ。
| |
|
2011/6/19(Sun) 12:20:49|NO.39721
>モジュール変数で配列番号が取得できないのは
モジュール内のthismodを関数の第一引数として渡して、
mref 変数A, 1
とすると、変数Aにモジュールの配列番号が代入されるんですよ!(↑参照)
|
|
2011/6/19(Sun) 13:46:02|NO.39725
> mref 変数A, 1
> とすると、変数Aにモジュールの配列番号が代入されるんですよ!(↑参照)
> 標準では配列取得命令ないから削除のとき困るんですよね。
過去ログなんかでも見かけていたので知ってはいました。
正確には「標準命令では専用の命令が用意されていない」という意味で書いたつもりでいました。誤解を招きすみません。
代替手段がありながらどうして説明も専用命令も無いんだろうということでした。
|
|
2011/6/19(Sun) 20:44:26|NO.39745
> mref 変数A, 1
> とすると、変数Aにモジュールの配列番号が代入されるんですよ!(↑参照)
次のコードを実行してみてください。
#module MTest id
#modinit
mref id, 2
return id
#global
max = 5
dimtype mod, vartype( "struct" ), max
repeat max
newmod mod, MTest : id = stat
mes "id = " + id
await 0
loop
配列番号はmref 変数, 2で取得できます。
|
|
2011/6/19(Sun) 21:19:16|NO.39747
>配列番号はmref 変数, 2で取得できます。
すごぉぉい!!
超便利かつベリーイージーじゃないですか…!
しかしここまで簡単だと逆に、GENKIさんが言ったように、
>代替手段がありながらどうして説明も専用命令も無いんだろう
というのが気になりますね…
thismodというシステム変数があるのですから、
thisidのようなシステムIDがあっても良いと思うんですけどね…
とはいえ、とってもイージーな方法を教えていただきありがとうございました(・ω・v
|
|
2011/6/20(Mon) 02:50:24|NO.39758
アイテムというのは、リストボックスのような多項目からなる一つ一つの項目を示す名称の意味で使わせていただいた単語でした。
木村さんに掲示して頂いた例は、確かに機能として使えないことはありませんが、
パラメータ毎にアクセッサが必要となる仕様は、あらゆるデータの読み書きが頻繁に行われるゲームにおいて不便なものではないかと思うのです。
そして不便なだけという問題なら、配列複数+プレフィックスでもあまり変わらない気がするのです。
せめて
item(cnt)->名称
のように、アロー演算子のようなもので直接モジュール内にアクセスできれば良いのですが。
むしろ、これができればほぼ解決するといっても過言ではないです。
|
|
2011/6/20(Mon) 09:08:11|NO.39761
>直接モジュール内にアクセスできれば良いのですが
まっことそうですよね
毎回
#modcfunc getItemName
return ItemName
のような関数を書くのは面倒ですからね;
とはいえ、本当はそうして関数を介して入出力を行った方がバグが少ないと言われていますが…
やはり実行速度が気になりますよね; mrefとかでなんとかならないのでしょうか…
|
|
2011/6/21(Tue) 06:48:25|NO.39778
>>zxさん
>パラメータ毎にアクセッサが必要となる仕様は、あらゆるデータの読み書きが頻繁に行われるゲームにおいて不便なものではないかと思うのです。
私はパラメーター毎に関数が必要な仕様がゲーム作成で不便とは思えません。というのも、パラメーターの書き込み用の記述方法と読み出し用の記述方法が分かれている事が、予期せぬパラメーターの改竄を防ぐと思うからです。
勿論、取得関数を一々作る必要有り、二次要素の読み出しが面倒、文章が長くなる、等の難点は多々あります。しかし、これらの難点は記述レベルの物です。パラメーターの改竄というバグレベルの難点を記述レベルの難点を受け入れる事で除去できるのなら、それほど悪くない選択だと思います。
まあ、zxさんのおっしゃる通り、オブジェクト指向を目指しているモジュール変数と共に、記述量が少なくて入門者にも分かりやすい構造体の双方が実装されれば問題は無いと思います。
……でも、やっぱり個人的には構造体より、クラスっ気のあるモジュール変数の方が便利な気がするんですよね。HSPに毒され過ぎたかなぁ…………
>>ひらまるさん
>モジュール内のthismodを関数の第一引数として渡して、
>mref 変数A, 1
>とすると、変数Aにモジュールの配列番号が代入されるんですよ!(↑参照)
>>xxxさん
>配列番号はmref 変数, 2で取得できます。
これは素晴らしい。大変便利なものを教えていただきました。ありがとうございます。これでモジュール変数の拡張性がまた一段上がりました。
>>ひらまるさん
>やはり実行速度が気になりますよね; mrefとかでなんとかならないのでしょうか…
ポインタ経由の有無による実行速度の減衰は微々たるものだと思います。少なくとも、画像処理や音響処理の影響量に比べれば、塵芥と言わないまで巨象と蟻程度の違いがあると思います。
参考までに、配列変数とモジュール変数の処理の速さを比較したスクリプトを載せておきます。自分の環境下(N=10000,W=25600)では
・書込(普通の変数):327
・書込(モジュール):796
・読出(普通の変数):0
・読出(モジュール):45
と、大した違いにはなりませんでした。
※上の結果を簡単に要約すると書込を約64回する毎に1msの時間の遅れが生じると言う意味
※読出の場合、約667回する毎に1msの時間の遅れが生じると言う意味
#include "d3m.hsp"
#define N 10000
#define W 25600
#module m_A f0, f1, f2
#modinit int i0, int i1, int i2
f0 = i0
f1 = i1
f2 = i2
return
#modcfunc get_a_0
return f0
#modcfunc get_a_1
return f1
#modcfunc get_a_2
return f2
#global
mes "N="+N+",W="+W
wait 300
start = d3timer()
dim variable, 3, 1
repeat N
variable(0,cnt) = rnd(100), rnd(100), rnd(100)
if (cnt\W) = 0 : await 20
loop
mes "書込(普通の変数):"+(d3timer()-start)
start = d3timer()
repeat N
newmod module, m_A, rnd(100), rnd(100), rnd(100)
if (cnt\W) = 0 : await 20
loop
mes "書込(モジュール):"+(d3timer()-start)
start = d3timer()
foreach variable
k = variable(0,cnt)+variable(1,cnt)+variable(2,cnt)
if (cnt\W) = 0 : await 20
loop
mes "読出(普通の変数):"+(d3timer()-start)
start = d3timer()
foreach module
k = get_a_0(module)+get_a_1(module)+get_a_2(module)
if (cnt\W) = 0 : await 20
loop
mes "読出(モジュール):"+(d3timer()-start)
stop
| |
|
2011/6/21(Tue) 21:46:34|NO.39794
>配列番号はmref 変数, 2で取得できます。
有難うございます。始めて知りました。
毎回、内部にIDを設けてループで検索していた 自分としてはとてもありがたいです。
>この先に構造体型変数なるものができれば良いなあと思っています。
自分も思います。API を使用する際に、色々と面倒くさいので、
メモリ構造も合わせてほしいです。
ついでに C のsizeof 的な物もあれば。完璧だと思います。
|
|
2011/6/21(Tue) 23:06:29|NO.39799
>木村さん
>参考までに、配列変数とモジュール変数の処理の速さを比較したスクリプトを載せておきます。
検証しようかなぁ…でも時間計測のためだけにスクリプト打つの面倒だなぁ…
なんて思っていたところに、詳細なデータとスクリプト本当にありがとうございます!
自分の環境では、
・書込(普通の変数):297
・書込(モジュール):439
・読出(普通の変数):0
・読出(モジュール):37
でした。
いろいろいじっていて気づいたのですが、dim variable, 3, 1の部分を
dim variable, 3, Nとしないと、配列を拡張しながら代入していくので速度がかなり変わることに気づきました。
さらに、newmodによるモジュールの作成とmodfuncによるモジュールの代入では、速度に数十倍の差があることもわかりました。
勝手ながら、その他いろいろと改造させていただき、
以下のようなスクリプトを組んでみました。問題があったらおっしゃってください。
#include "d3m.hsp"
#define global N 1000000
#module m_A f
#modinit
dim f, N
return
#modcfunc get_a_f int i0
return f(i0)
#modfunc set_a_f int i0, int i1
f(i0) = i1
return
#modfunc get_a_Address var o
dup o, f
return
#global
mes "N="+N
dim variable, N
start = d3timer()
repeat N
variable(cnt) = rnd(100)
loop
mes "書込(普通の変数):"+(d3timer()-start)
newmod module, m_A
start = d3timer()
repeat N
set_a_f module, cnt, rnd(100)
loop
mes "書込(モジュールへ命令で):"+(d3timer()-start)
get_a_Address module, f
start = d3timer()
repeat N
f(cnt) = rnd(100)
loop
mes "書込(モジュールへアドレスで):"+(d3timer()-start)
start = d3timer()
repeat N
k = variable(cnt)
loop
mes "読出(普通の変数):"+(d3timer()-start)
start = d3timer()
repeat N
k = get_a_f(module,cnt)
loop
mes "読出(モジュールから関数で):"+(d3timer()-start)
start = d3timer()
repeat N
k = f(cnt)
loop
mes "読出(モジュールからアドレスで):"+(d3timer()-start)
実行するとわかっていただけると思いますが、関数での操作は通常に比べて2倍近い時間がかかります。
しかしdup命令によるクローン変数を使うと、速度を限りなく通常変数の操作に近づけることができます。
なんとなく、「dupしたら早いかな〜?」などと思っていたところだったので、この実験はとても有意義なものになりました。
元のソースとモチベーションを提供してくださった木村さん、本当にありがとうございます。
| |
|