HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2018
0929
Tsuyoshiモジュール型変数のモジュール変数へ簡単にアクセスする方法10解決


Tsuyoshi

リンク

2018/9/29(Sat) 21:41:05|NO.85479

現在、プレイヤーキャラの状態を保存する際に、整数や実数を含む情報を1つの変数で管理したいと
考えています(C言語の構造体みたいにしたい)

そこで、モジュール型変数を使ってプレイヤーキャラの状態を保存しようと思っているのですが、
newmodで作成したモジュール型変数でモジュール変数を扱う際に、もっと簡単に値を参照
したり、変更したいと考えています。

例えば、下記のようなモジュールを作成し、モジュール変数pos_xなどを取得する場合、
「get」のような新規関数を作成しなければ参照できません。



#module playerInfo pos_x,pos_y #modinit pos_x = 0 pos_y = 0 return // 変数の値を返す #modcfunc get str name switch name case "pos_x" : ans = pos_x :swbreak case "pos_y" : ans = pos_y :swbreak swend return ans #global newmod py1, playerInfo mes get(py1,"pos_x")


この mes get(py1,"pos_x") をもっと簡単に、モジュール内の変数を参照する感じで
mes pos_x@py1 のように扱うことはできないでしょうか。

可能なら pos_x@py1 = 10 のように代入などもできるとうれしいです。


よろしくお願いします。



<余談>
正直、上記の方法とpy1_pos_xのように専用の変数を作る方法のどちらが最終的に楽なのか
決めかねています。毎回関数や命令で値を参照してると読みづらくなりそうで...

モジュール型変数の方法なら、敵キャラクターの情報も newmod enemy(0), playerInfo
みたいな感じで増やせるので、値を直接参照できるならこの方法でやりたいと思っています。

もし他に良い方法があれば、そちらも是非教えていただけるとありがたいです。



この記事に返信する


YOS G-spec

リンク

2018/10/2(Tue) 22:06:51|NO.85494

結論から言うと、モジュール変数を外部からそのままアクセスすることは不可能です(全てprivate変数)。
しかし、いくつかの提案はありますので目を通していただければ幸いです。

1. 個別にアクセサを用意する。

#runtime "hsp3cl" #module playerInfo _pos_x,_pos_y // pos_xアクセサ #modcfunc pos_x return _pos_x #modfunc set_pos_x int v _pos_x=v: return // pos_yアクセサ #modcfunc pos_y return _pos_y #modfunc set_pos_y int v _pos_y=v: return // 代入演算子 #define global reset(%1,%2,%3,%4) set_%2%1,%2(%1)%3%4 #modinit _pos_x=0 _pos_y=0 return #global newmod py1,playerInfo mes pos_x(py1) set_pos_x py1,10 mes pos_x(py1) reset py1,pos_x,+,12 mes pos_x(py1) reset py1,pos_y,-,12 mes pos_y(py1)
愚直ですが、この方法が一番きれいだと思います。
僕はこの方法で一貫してモジュール変数を公開させています。
書き換え不能な変数としたい時も単にsetterを削除すればよいので
アクセス権限も比較的操作しやすいです。

2. モジュール空間内でラベルをコールバックさせる。

#runtime "hsp3cl" #module playerInfo pos_x,pos_y #modinit pos_x=0 pos_y=0 return // モジュール空間内でラベルをコールバック #modfunc with label l gosub l: return #global newmod py1,playerInfo if 0{*py1_l mes pos_x@playerInfo pos_x@playerInfo=10 mes pos_x@playerInfo return} with py1,*py1_l /* @playerInfoを省略する書き方 *//* #define pos_x pos_x@playerInfo #define pos_y pos_y@playerInfo if 0{*py1_l mes pos_x pos_x=10 mes pos_x return} with py1,*py1_l *//* または、 *//* #define ctype pi(%1) %1@playerInfo if 0{*py1_l mes pi(pos_x) pi(pos_x)=10 mes pi(pos_x) return} with py1,*py1_l */
始めに直接モジュール変数を操作することは不可能だと申しましたが、
この方法を使えば無理やりですが可能です。
モジュール内でモジュール変数にアクセスできる状態でgosubで飛ばすと
モジュール外でもラベルに飛ばされている間だけ使用することができます。
使用する時は用法・用量を守ってお使いください。

3. 列挙体と配列で構造体もどき。

#runtime "hsp3cl" #enum pos_x = 0 #enum pos_y // 1 // #enumの個数 #enum pi_length // 2 #module #deffunc playerInfo array v v=0,0: return #global playerInfo py1 mes py1.pos_x py1.pos_x=10 mes py1.pos_x //多次元配列は始めから上限を指定(dim,sdim,ddim)する必要がある。 #module #deffunc playerInfo_enemy array enemy,int n dim enemy,n,pi_length repeat n enemy.cnt.pos_x=256 enemy.cnt.pos_y=256 loop return #global playerInfo_enemy enemy,10 // 配列は括弧で括ってもドットで繋いでも良い。 mes enemy.0.pos_x // 256 mes enemy(1,pos_x) // 256 // 配列の自動拡張を利用したい場合は最大次元が可変次元となる必要がある。 #module #deffunc playerInfo_enemy2 array enemy dim enemy,pi_length,1 enemy.pos_x.0=512 enemy.pos_y.0=512 return #global #module #deffunc playerInfo_enemy2_add array enemy enemy_length=length2(enemy) enemy(pos_x,enemy_length)=512 enemy(pos_y,enemy_length)=512 return #global playerInfo_enemy2 enemy2 mes length2(enemy2) // 1 playerInfo_enemy2_add enemy2 mes length2(enemy2) // 2 // 最大次元の0は省略可 mes enemy2.pos_x // 512 mes enemy2(pos_x,1) // 512
古典的な方法ですが、列挙体(#enum)と配列で保持する方法があります。
難点としては型が固定されることや、
配列であるため取り回しが少し悪いことにありますが、
希望される書き方には一番近い物かと思います。

4. マクロ(#define)で専用の変数を生やす。

#runtime "hsp3cl" #define ctype playerInfo(%1) \ %1_pos_x=0 :\ %1_pos_y=0 playerInfo(py1) mes py1_pos_x py1_pos_x=10 mes py1_pos_x #define ctype playerInfo_add(%1) \ %1_pos_x(length(%1_pos_y))=0 :\ %1_pos_y(length(%1_pos_y))=0 playerInfo(enemy) playerInfo_add(enemy) mes enemy_pos_x.0 // 0 mes enemy_pos_x.1 // 0
余談で言っていらした、"専用の変数"をマクロ(#define)で用意させる方法です。
変数を新しく生成しているので、配列のように型による制限はありません。
その代わり、py1を一つの物体として扱うことは難しくなるでしょうか。
py1に対してまとめて操作を与えるときは何をするにもマクロに頼らざるを得なくなると思いますので、
それがデメリットとなるでしょう。
なお、マクロで変数名を繋げる場合、#defineでctypeを使わないと
隙間が空いて変数名が切れてしまうのでctypeが必須となります。


あとは余談ですが、最初のget,setを使う場合は、
switchを使用するよりもif&returnしてしまう方が簡潔に書けますね。

#runtime "hsp3cl" #module playerInfo pos_x,pos_y #modinit pos_x=0 pos_y=0 return // 変数の値を返す #modcfunc get str v if v="pos_x": return pos_x if v="pos_y": return pos_y return -1 // 変数の値に代入 #modfunc set str v,int x if v="pos_x": pos_x=x: return if v="pos_y": pos_y=x: return return 1 // 代入演算子 #define global reset(%1,%2,%3,%4) set %1,%2,get(%1,%2)%3%4 #global newmod py1,playerInfo mes get(py1,"pos_x") set py1,"pos_x",10 mes get(py1,"pos_x") reset py1,"pos_x",+,12 mes get(py1,"pos_x") reset py1,"pos_y",-,12 mes get(py1,"pos_y")



さか

リンク

2018/10/3(Wed) 22:31:24|NO.85498

自分もC言語を使うので構造体あればいいなと思いますが、単純な代入を関数作ってまで
行うのは面倒ではないでしょうか。

配列ですが以下のようだとCの構造体ぽく記述も行えると思いますがどうでしょう。

#enum pos_x=0
#enum pos_y

py1.pos_x=1
py1.pos_y=2

mes py1.pos_x
mes py1.pos_y

test

stop
#module
#deffunc test

mes py1@.pos_x@
mes py1@.pos_y@

#global



さか

リンク

2018/10/3(Wed) 23:05:58|NO.85499

あ、YOS G-specさんが3番目で挙げてますね。失礼しました。
YOS G-specさんが記載しているように実際は配列なのでちょっと気持ち悪いです
が自分はこれが一番単純でいいと思います。



Tsuyoshi

リンク

2018/10/4(Thu) 00:06:52|NO.85500

YOS G-spec さん


ご回答ありがとうございます。
直接はアクセスできないのですね。
ありがとうございます。とりあえず直接アクセスすることに関しては諦めます。


ご提案内容の中では、後々のことを考えると1が良さそうと感じました。
#defineの部分で、%1%2のように文字を繋げて関数にできるのは勉強になりました。
てっきり%1*%2みたいな演算しかできないと思っていたので。

あと実数も同じ代入演算子で処理できそうなので、良かったです。
当初は文字列で引数を与えて、"."があるか無いかで整数・実数を判断して処理する等を
考えていたので、工数が減って見た目も良くなり助かります。


2の方法はHSPらしいというか、好みがわかれそうですね。
ちゃんと管理すれば便利そうです。


3については、確かに構造体っぽくなりますね。
小規模なプロジェクトとかだとこの方法が便利そうです。
今回は、#enumで宣言した変数名をうっかり後で使ってしまいそうなのと、
整数・実数・文字列が入り乱れたもので作りたかったので見送らせていただきました。
(この部分、できるだけ短い変数名を付けたいとこなので、被りが結構怖いですね...)


4については、こういう変数の増やし方もあるという勉強になりました。


switch文については、if文の連打を避けるあまり、本質を見失ってました。
そもそも比較する値が被ることはないですし、沢山比較するからswitch文だ!というのは
安易でした。




さか さん


ご回答ありがとうございます。
YOS G-spec さんのご提案内容にもありましたが、こういう書き方で型も全部使えたら
便利ですね。
昔は構造体の便利さなんて分からなかったので、今になって気づくとは思わなかったです。
HSP関連のスレッドではずっと議論されているものなので、使えるようになると嬉しいですね。




最後になりますが、お二人とも、ご回答ありがとうございました。
とても勉強になりました。

お二人のご回答で解決とさせていただきます。



Tsuyoshi

リンク

2018/10/4(Thu) 00:09:24|NO.85501

すみません、NO.85500の内容で少しだけ訂正を...


>%1%2のように文字を繋げて関数にできるのは



%1%2のように文字を繋げて関数や命令の名前の一部として扱えるのは



YOS G-spec

リンク

2018/10/4(Thu) 00:53:36|NO.85502

ひとまず解決されたようで何よりです。

2案についてマクロで囲えばいくらか見栄えが良くなったので再渇しておきます。

#runtime "hsp3cl" #module playerInfo pos_x,pos_y #modinit pos_x=0 pos_y=0 return #modfunc local with label l gosub l: return #define global with(%1) %twith if 0 {*%i%s1 #define global end_with %twith return} with@playerInfo %o,*%o #global newmod py1,playerInfo #define ctype pi(%1) %1@playerInfo with py1 mes pi(pos_x) pi(pos_x)=10 mes pi(pos_x) end_with

配列を使用した方法はやはり見た目的には最もスマートでいいんですけど、
いざ文字列と整数を実数を含めるときはどうするか悩みますね…
全部文字列で保存して随時キャストさせるか型ごとに配列を用意するか、でしょうか。

あとは、#enumだと名前かぶりが怖いとのことですが、これに関してはモジュール関数でも変わりませんね。
これらの対策は僕の場合だと2,3文字の接頭辞をつけてしまうことが多いです。

#enum、#define、#constなどの定義は#moduleに閉じ込めてしまうことも容易なので必要になるモジュール内だけで定義するのもありかもです。



YOS G-spec

リンク

2018/10/4(Thu) 01:07:50|NO.85503

あと、もう一点。
#enum、#define、#constなどと言ったプリプロセスで宣言される値は定数ですので変数として再代入することはできません。
コンパイル時に値に直接置き換えられています。



Tsuyoshi

リンク

2018/10/5(Fri) 21:56:40|NO.85512

すみません、定数でした...
コンパイル時にエラーも確認できたので、心配することではなかったですね。

マクロはパラメータ使うと大分捗りますね。
これまで全然使っていなかったので驚いてます。
再度書き込みいただいた方法もあわせて使い分けたいと思います。



YOS G-spec

リンク

2018/10/6(Sat) 09:54:53|NO.85515

#defineは使えるようになるとめっちゃ便利です。
その分デバッグが大変になったり、危険度は上がりますけど、
適度に使う分にはHSPの不足する機能を大体埋められるので強力ですね。
もしデバッグで困ることがあったら
#cmpopt ppout 1

を使用してみてください。 プリプロセス後のソースが得られます。 おまけ: https://codetter.com/?p=1330



Tsuyoshi

リンク

2018/10/7(Sun) 15:18:51|NO.85527

サンプルコードまで...
ご足労おかけします。

マクロは#constぐらいしか使ってなかったので、せっかくの機会ですし使ってみようと
思います。

アフターフォローまで、ご丁寧にありがとうございました。



ONION software Copyright 1997-2023(c) All rights reserved.