スクリプトの作り方(基本編)

AoSプログラムの基本的なお話

AoS世界はpythonっていう言語で動いている
大文字と小文字に区別がある
インデントが大事



基礎の基礎

+ ...
いろんな言葉

インデント
if
else
elif
and
or
not
=
==
関数
引数
戻り値
コメント

インデント

+ ...
プログラムの行頭に入れる空白のこと
AoSの動いてるpythonだとめちゃ重要でこれがしっかりしてないとプログラムが動かない
プログラムがインデントで階層分けされてて、同じ高さの部分が、その一個上の階層の中身になってる
def (関数名)やif (条件)などの下のインデントを下げることで、インデントが下がっている区画は上の関数や条件文に属することを意味する。

Tabキーを使ったインデントとスペースキーを使ったインデントでは見た目が同じでもコンピュータ上では違う風に処理されてインデントエラーとか言われる場合があるので注意

:(コロン)

+ ...
階層が変わることを表す
def (関数名)やif (条件)などの行末に付けて次の行からインデントを一つ下げる
インデントが下がる直前に : が無いとエラーになる

if

+ ...
もし~~ならって意味の部分
pythonでは
if (条件):
	(動作)
	(動作)	
みたいになってる
条件を満たすならその下の階層を読み込んで中身を実行
条件を満たさないならスルー
条件は基本的に論理式で作られて、満たすなら1(True)、満たさないなら0(False)って感じになる

else

+ ...
ifの後にくっついたりする
さもなくば、みたいな意味
if (条件):
	(動作1)
else:
	(動作2)
こうなると、条件を満たすなら動作1を、満たさないなら動作2を実行

elif

+ ...
else + if みたいな感じ
条件を大量に用意する場合使う
if (条件A):
	(動作1)
elif (条件B):
	(動作2)
elif (条件C):
	(動作3)
else:
	(動作4)
これだともし条件Aを満たすなら動作1、Aを満たさずBを満たすなら動作2、A,Bを満たさずCを満たすなら動作3、どれも満たさないなら動作4になる。

上のやつをelifを使わないで書くとこうなる
if (条件A):
	(動作1)
else:
	if (条件B):
		(動作2)
	else:
		if (条件C):
			(動作3)
		else:
			(動作4)
どちらでも動作は同じ

and

+ ...
且つ
条件式をが両方満たされたら条件成立にする
if (条件A) and (条件B):
	(動作1)
else:
	(動作2)
これだと条件ABが双方満たされれば動作1を、どちらかでも欠けてれば動作2を行う

論理式はゼロイチで作られるって上で書いたけど、これは掛け算にあたる
一個でもゼロが入ったらゼロ

or

+ ...
または
条件式のどちらか一方がOKなら成立にする
if (条件A) or (条件B):
	(動作1)
これだと、A,Bの一方が満たされれば、もう一方はだめでも動作1が実行される。

not

+ ...
否定文
条件を満たさないなら成立
if(条件A)and not (条件B):
	(動作1)
これだと「Aを満たす」かつ「Bを満たさない」ならば条件成立になる。

=

+ ...
プログラムの世界では数学のやつと違って「 = 」は代入を意味する。
例えばmedkitの所持数にを3を代入したければ
      medkits = 3
って書く
当然数値を計算することも可能
medkit使って所持数が一個減る時は
if (medkitが使用された):
	medkits = medkits - 1
となる。
また、上の例のように、自分自身に自分自身を変化させたものを代入する場合、
(上の例だとmedkitsに「計算前のmedkits」 - 1を代入している)の場合は、
medkits -= 1
と書くことができる。既存スクリプトではこっちの書き方が使われることが多い
増やすときも同様に
nantara += kantara
と書くこともできる。この場合は
「計算後のnantara」は、「元のnantara」 + kantara になる

加減乗除の計算記号は
+ - * /
小数点以下切り落とし割り算と、割り算の余りを計算する記号は
// %
例えば
a = 7 // 3
だとaは2になり、
a = 7 % 3
だとaは1になる

==

+ ...
これは数学で使うのと同じような感じの意味な気がするイコール
ifの条件のところみたいな論理式で使われる
a == b って書くとaがbと等しいかどうかを問うことになる
if a == b:
って書いたら、aイコールbのときifの中身を実行
他には
 <   <=   >=   >  != 
の記号がある
前の4つは見てのとおり大なり小なりの意味
if a <= 3:
	(動作1)
elif a < 8:
	(動作2)	
これだとaが3以下なら動作1、3より大きくてかつ8未満なら動作2、8以上ならスルーということになる
イコールがつくかつかないかでその数が入るか決まるのでそこ重要
!=の!はnotを示す。すなわち≠。

ちなみにif文の中でaが3の時動作させようと思って
if a = 3:
とか書くとエラ―吐くぞ。ifの時は==だぞ、間違えるな。



関数、引数、戻り値

+ ...
プログラム長くなると読みづらいし書くのもめんどくさいしいいことがないから、よく使う部分をモジュール化して置いておこうというプログラムの概念
プログラムの中の小さいプログラム的な
pythonだとdef()なんちゃらの部分が関数にあたる

例えば、a+bとa*bの大きい方をXに代入する場合
A = a+b
B = a*b

if A>=B:
	X = A
else:
	X = B
となるが、これがプログラムの中で何回も使われる場合、Xを求める関数を作ってしまう方がよい

def nanika_betuno_kansuu:
	~動作1~
	dmg = 5
	~動作2~
	rate = 2
	damage = keisan(dmg, rate)
	~動作4~

 ~~~~~~~中略~~~~~~~~~~~~~~~~
def keisan(a,b)
	A = a+b
	B = a*b

	if A>=B:
		X = A
	else:
		X = B
	return X
って書いてしまえば、長々しい計算部分は一度書けばよくて、後はX=keisan(3,4)とでも打てば1行でXに答えが計算される

この場合プログラムの走る経路は
関数「nanika_betuno_kansuu」が実行される
	動作1
	変数dmgに5を代入
	動作2
	変数rateに2を代入
	変数damageにkeisan(5, 2)を代入しようとする
	関数keisan(___, ___)を実行
		keisan(a,b)の a,b に 5,2 を代入
		Aに 5+2 すなわち 7 を代入
		Bに 5*2 すなわち 10 を代入
		AはB以上か? 7 >= 10 ではないのでelseへ
			XにBすなわち 10 を代入
		Xすなわち 10 を元の関数に返却する
	keisan(dmg, rate)はすなわちkeisan(5,2)、すなわち 10 
	damageに10が代入される
	動作4
という流れ

このkeisan()みたいなのを関数
keisan(a,b)のa,bみたいに突っ込む値を引数
return Xみたいに計算終了後keisan()の結果となる数値を戻り値という

関数で引数を3つ指定してんのに、使うときに引数を2つしか送らなかったりするとエラー吐くからこれ大事

既存のスクリプトの読み方

+ ...
ここまでで既存スクリプトの断片が読めるはず
例としてmedkit.pyの一部分を切り取った
def medkit(connection):
    if connection.medkits and connection.hp < 100:
        connection.set_hp(connection.hp + connection.protocol.heal_amount,type = FALL_KILL)
        connection.medkits -= 1
        connection.send_chat('You have been healed')
    else:
        connection.send_chat("You don't have any medkits or have full health!")

構造としては
一番上位の階層のdef~~が最上階層。medkitが使用される際に呼び起される関数。
  2層目に if と else がある
    最下層は実際に実行して何かを動作させる部分にあたる

connection.set_hp(~~)はHPを括弧の中身に変化させる関数
,で区切られてるから、値とタイプの二つを引数として必要としてるっぽいな
connection.protocol.heal_amountはスクリプトのどっか別の場所で指定されてる回復量
connection.send_chat('~~')はプレイヤーにメッセージをを送る関数。これが読まれたら''の中身がチャットに赤字で表示される。
この辺の関数はゲームにもともと用意されてるっぽいから関数のカッコに好きな値突っ込めば動作する感じ

connectionがなんだかは後述

日本語に起こすと

 medkitの関数
 	もしmedkit所持数が1以上 かつ HP が100未満ならば:
 		HPを(今のHP + 回復量)にタイプはFALL_KILLで変更
	medkit所持数を1減らす
	「You have been healed」とチャット送る
「medkit所持数が1以上 かつ HP が100未満」を満たさない(どっちか一方でも満たさない)ならば
        「You don't have any medkits or have full health!」とチャットを送る
となる

コメント

+ ...
プログラム的には読み込まれない、読みやすくするためのメモ的な機能
スクリプトの一行目に
  # -*- coding: utf-8 -*-
って入れると日本語のコメントを入れれるようになる
これ入れないで日本語コメント使うとバグる
コメントの入れ方
「#」を入れるとその行はそこから先無かったことになる

"""
"""
で挟むとその間はなかったことになる
'''
'''
で挟んでもその間はなかったことになる
"""とか'''で挟む場合はインデント合わせないとエラー吐くので注意
# -*- coding: utf-8 -*-
     """
 この間は何書いても基本的にプログラムに支障ない
 上の方にプログラムの説明書いとくといいね
     """
	def medkit(connection):
	    if connection.medkits and connection.hp < 100:  #ここに説明とか書くとgood
	        connection.set_hp(connection.hp + connection.protocol.heal_amount,type = FALL_KILL)
	        '''
    コメントだから何書いてもセーフ
         うんこちんこまんこ
    チョンチョンチョンのインデントを周りに合わせないとエラーする
	        '''
	        connection.medkits -= 1    
	     #  connection.send_chat('You have been healed') これでこの行はなかったことにできる。簡単だからエラーの原因探るのとかに便利
	    else:
	        connection.send_chat("You don't have any medkits or have full health!")


print (コンソール(run.exeの黒いウィンドウ)に文字列や内部数値などを表示)

+ ...
print "hogehoge"
とやると、黒いウィンドウにhogehogeと表示される。
読み込まれたらすぐに表示されるので、エラーの原因となる部分の1行でも上にあれば表示される。
逆に、一行でもエラー部分の下にあれば表示されないので、この文字列がログに表示されるかどうかを見ることでどこがエラーの原因かを探ることができる。
あるいは、if,elifの中に別々の文字列を突っ込んだら、どのルートをプログラムが読んでるのかを把握することができる。

例
if (条件A):
	print "ルートA"
elif (条件B):
	print "ルートB"
elif (条件C):
	print "ルートC"
else:
	print "ルートE"


コンソール表示
ルートB
このようになれば、このプログラムがその時の条件では条件Bを満たしていたことがわかる。



print x    (xは変数)
とやると、xの代入値が表示される。
例えばプレイヤーの座標とかを表示させればプレイヤーの座標をコンソールで把握できる。
これのいいところは、数値として表示されないスポーン座標や内部計算などを数値として表示させることができるので、自作スクリプトがどこまで意図したとおりに動作しているかを把握することができる所である。

基本的にスクリプトのどの部分に突っ込んでも正常に動作してくれるので安心。

print "otimmpo!" 
のようにダブルクォーテーション""またはシングルクォーテーション''で囲むと文字列をprintできる。便利。
日本語入れるとさすがにエラー吐く。半角英数のみ


import

+ ...
インポート
標準ライブラリ(デフォルトで用意されてる関数の辞書的なもの)を呼び出すのにつかう。
あと他の.pyファイルを連動させる場合に使う(A.pyの中の特定の関数が読まれたらB.pyのある関数に飛ぶ、みたいな)。

実際のスクリプトを見ると、スクリプトの上の方で
import csv
from StringIO import StringIO
from collections import deque, defaultdict
from functools import partial
from itertools import izip, islice, chain
from random import choice
from twisted.internet.reactor import callLater, seconds
from pyspades.world import cube_line
こんな感じになってると思う。これがインポート。

例えばランダムな値を生成したい場合、
import random
これをスクリプトのインポートのところに書くと、http://docs.python.jp/2/library/random.htmlにある機能が使えるようになる。
例えばある値xに100~200の間のランダムな値を代入したい場合、
x=random.randint(100, 200)
と書けばよい。ランダムとかの各機能の詳細は別項で書くと思う。

import randomならrandomライブラリの中のどの機能も使えるようになるが、機能(例えばrandint)の前にいちいちrandom.randintのようにインポート元を書かないといけない。
ここで、
from random import randint, uniform, vonmisesvariate
のように各機能をインポートすると、
x=randint(100, 200)
のようにインポート元を書かなくても使えるようになる。
しかしこの場合、インポートした機能(この場合randint, uniform, vonmisesvariate)しか使えない

よく使われるライブラリとしては、
randomのほかに、サインコサインとか√とか数学系の機能を使えるようになるmathや、n秒後に関数呼び出しのなどの時間系の機能を使えるreactorなどがよく使われる。
あとAoS内固有のライブラリ
from pyspades.common import Vertex3
from commands import add, admin
from pyspades.world import Grenade
from pyspades.server import grenade_packet
なんかもあったりする。

なお、筆者がよくわかっていないので詳しく書けないが、標準ライブラリの中でも使えるライブラリと使えないライブラリがあるっぽい気がする
多分pyspadesのコンパイラにデフォルトで用意されてたりされてなかったりするってことでしょう






AoSスクリプトのなんやかんや

+ ...

動作をしたら割り込みを起こす感じのやつについて

+ ...
スクリプトを作る場合プレイヤーが何か行動を起こしたときに何かが起こるようなものを作りたい場合がほとんどだと思う
ゲームの方にもともと「プレイヤーが○○をしたときに呼び出される関数」ってのが用意されてるからそれを使うといい感じにできる

例えば
def on_spawn(self,pos):
ってのはスポーンした瞬間にスポーン位置posって情報が入力されて呼び出される関数

def on_spawn(self,pos):
	動作1
 	return connection.on_spawn(self,pos)
ってやるとスポーンしたら動作1が行われる

例として、緑チームのときだけなんかチャットが出る文を作った
def on_spawn(self,pos):
	if self.team == self.protocol.green_team:
		self.send_chat("you spawned in green team")
	return connection.on_spawn(self,pos)

他には
def on_animation_update(self,jump,crouch,sneak,sprint):
はジャンプ、しゃがむ(またはしゃがみ解除)、V(またはV離す)、ダッシュ(またはダッシュ解除)の状態変化が起こった瞬間に呼び出される

def on_animation_update(self,jump,crouch,sneak,sprint):
	if self.team == self.protocol.blue_team:
 	    	if jump:
			self.send_chat("space osita")
		elif sprint:
			self.send_chat("shift osita")
		elif sneak:
			self.send_chat("V osita")
		elif crouch:
			self.send_chat("ctrl osita")
		else:
			self.send_chat("Shift ka V ka Ctrl wo hanasita")
	return connection.on_animation_update(self,jump,crouch,sneak,sprint)
この例だと青チームの人が上記動作を行うとそれに応じたメッセージが表示される



他にもいろいろ

   def on_hack_attempt(self, reason):
   def on_position_update(self):    
   def on_weapon_set(self, value):    
   def on_fall(self, damage):
   def on_orientation_update(self, x, y, z):
   def on_shoot_set(self, fire):
   def on_secondary_fire_set(self, secondary):
   def on_walk_update(self, up, down, left, right):
など用意されている(ここに書いたのはごく一部)
戻り値を与えないとバグったり、エラー吐かないのに他のスクリプトが正常に動かなくなったりすることがあるので注意
基本的には
def xxxxxx(self, aaaa, bbbb):
っていう関数には
    return connection.xxxxxx(self, aaaa, bbbb)
と戻り値を与えておけばよい

https://github.com/Colorpinpoint/pysnip/blob/master/pyspades/server.py
ここの1100行目辺りにトリガーとして使える関数一覧が載ってる。

AoS世界の座標について

+ ...
AoS世界のマップは高さ64,縦横512ブロックの箱庭であるのは常識である。

マップの左右方向がx軸、上下方向がy軸、そして天地方向がz軸である
マップ左上の上空が原点となり、マップ右がx軸正方向、マップ下がy軸正方向、マップの地面方向がz軸正方向となる。
また、z=62は一番下の掘れない地面で、z=63は水面ブロックである。

512*512*64の領域はブロックを置けたりグレネードが爆発できたりする領域の話で、プレイヤーが存在できる世界はこれだけではない。
z>63の、海よりも深い領域も存在する。スクリプトを使うと掘れたりする。なんか真っ黒で、プレイヤーも真っ黒になった。
しかしその領域に足を踏み入れるとバグって鯖が落ちるので注意。

z<0のzがマイナスになるゾーンも存在する。この領域では問題なくプレイヤーが動くことができる。
しかし、z<0領域ではグレネードにダメージが乗らないほか、ブロックを置くことができない。
また、あまり高く行き過ぎる(z<-80くらい)とクライアントが落ちる事があるので注意。
マップ左右(0<x<511)からはみ出した領域では、コマンドなどは打てるが移動ができなくなる。
マップ上下(0<y<511)からはみ出すとクライアントが落ちる。



AoS世界の座標まとめ

 通常世界は
 0<x<511
 0<y<511
 0<z<63
 の領域。

 x軸は右が正
 y軸は下が正
 z軸は地面が正

現在位置取得するやつについて

+ ...
x, y, z = self.world_object.position.get()

この一行で変数x,y,zにselfで指定されるプレイヤーの座標が代入される。
pos = self.world_object.position
でも同様にpという変数に座標が代入されるが、この座標はvertex3型である(俺もよくわからん)
つまるところ pos = vertex3(x,y,z) である

もちろん変数名は適当なものでおkである


なお、この座標はプレイヤーの頭の位置であるので、足元の座標だとx,y,z-2くらいになる

現在方向取得するやつについて

+ ...
プレイヤーの向いてる方向を取得する
方向は半径1の球の表面の点の座標で表されることが多い
例えば真上を向くときは(0,0,-1)であるし、マップ右側を向くときは(0,0,1)、マップ左下正面を向くならば(-0.7,0.7,0)くらいである。


x2,y2,z2 = self.world_object.orientation.get()
座標の取得と同様にこの一行でおk
ori = self.world_object.orientation
でもおk
座標と同様に ori = vertex3(x2,y2,z2)

グレネード生成するやつについて

+ ...
基本的なこととして、AoS世界のグレネードは「ダメージの発生する見えないグレネード」と「目に見えるグラフィックだけのハリボテグレネード」の二種類が存在して、
この二つが重なって同じ向きに同じ速さで投射されることでいつものグレネードが出来上がっている。

self.protocol.world.create_object(Grenade, time, pos, None, ori, self.grenade_exploded)
これが目に見えないダメージのある方のグレネード生成部分
括弧内のGrenadeはさておき、二つ目のtimeが生成から爆発までの時間。より正確にいうと、生成から最後のself.grenade_exprodedという関数を呼び出すまでの時間。
最後の引数の関数を変えれば爆発時に別の関数を呼び出すことも可能
三つ目のposはグレネードの生成点の座標(vertex3型)
四つ目のNoneはよくわからん(誰か教えて)
↑この部分にposやoriと同形式のデータを与えるとその分が初速に加算されるらしい まあ気にしなくてよいかと
五つ目のoriはグレネードの初速

というわけで、この関数ひとつで、目に見えないtime秒後に爆発するグレネードがposからoriに向けて発射される。
例えば
self.protocol.world.create_object(Grenade, 2.0, Vertex3(255,255,32), None, Vertex3(0,0,-2), self.grenade_exploded)
とやると、座標(255,255,32)の点から(0,0,-1)(真上向き)に結構な速度でグレネードが発射され、2秒後に爆発される。
だがこれだけだと目に見えない。

目に見えるハリボテグレネードの生成はコチラ
grenade_packet.value = time
grenade_packet.player_id = self.player_id
grenade_packet.position = pos.get()
grenade_packet.velocity = (a,b,c)
self.protocol.send_contained(grenade_packet)
1行目は爆発までの時間
2行目は誰のグレネードかを書く
3行目は生成座標。vertex3ではなく普通の座標。
4行目は初速。こちらも普通の座標
5行目はよくわからんけどこのまま突っ込んどきゃいいと思う

というわけでこれらを合わせると、目に見えて爆発グラフィックも出るしダメージも喰らうグレネードができる。

例として3秒で爆発するグレネードをプレイヤーの頭の2ブロック前から向いてる向きに、すごい速さですっ飛ばすスクリプト部分はこんな感じ
	SPEED=5
	x, y, z = self.world_object.position.get()
	p = Vertex3(x+a*2,y+b*2,z+c*2)
	a,b,c = self.world_object.orientation.get()
	a = a * SPEED
	b = b * SPEED
	c = c * SPEED
	self.protocol.world.create_object(Grenade, 3.0, p, None, vertex3(a,b,c), self.grenade_exploded)
	grenade_packet.value = 3.0
	grenade_packet.player_id = self.player_id
	grenade_packet.position = p.get()
	grenade_packet.velocity = (a,b,c)
	self.protocol.send_contained(grenade_packet)


グレネードの着発信管の作り方


グレネードを時間じゃなくて、砲弾みたいにブロックと衝突した瞬間に爆発するようにしたいことは多いはず
そのやり方がこれ
	grenade = self.protocol.world.create_object(Grenade, 0.0, pos, None, ori, None)	
	collision = grenade.get_next_collision(UPDATE_FREQUENCY)
	if collision:
		impact, x, y, z = collision
弾道計算用仮想グレネードを生成して、弾道を計算させるというもの。
一行目で弾道計算用のダミーグレネード「grenade」を生成(posから、初速oriで投射爆発は起こらない)というもの
二行目でダミーグレネードの弾道上でブロックへの衝突が生じる地点の座標、衝突までの時間を計算
collosionの中身はimpact=衝突までの時間、x,y,z=衝突座標の二点
重要なのは時間。
衝突までの時間がわかったので、それを実際に発射するグレネードの爆発時間にすれば、ちょうど衝突の瞬間に爆発が起こる。

例として、右クリでスコープ除いてるときにVを押すと向いてる向きに着発グレネードが飛んでゆくスクリプト全文を示す。
from pyspades.constants import *
from pyspades.world import Grenade
from pyspades.server import grenade_packet
from pyspades.common import Vertex3
from pyspades.constants import UPDATE_FREQUENCY
           
SPEED = 1.5
def apply_script(protocol,connection,config):
	class GrenadeConnection(connection):
		def on_animation_update(self,jump,crouch,sneak,sprint):
			if sneak and self.world_object.secondary_fire:
				x, y, z = self.world_object.position.get()
				pos = Vertex3(x, y, z+1)
				a,b,c = self.world_object.orientation.get()
				a = a * SPEED
				b = b * SPEED
				c = c * SPEED
				forward = Vertex3(a, b, c)
				grenade = self.protocol.world.create_object(Grenade, 0.0, pos, None, forward, None)	
				collision = grenade.get_next_collision(UPDATE_FREQUENCY)
				if collision:
					impact, x, y, z = collision
				self.protocol.world.create_object(Grenade, impact, pos, None, forward, self.grenade_exploded)
				grenade_packet.value = impact
				grenade_packet.player_id = self.player_id
				grenade_packet.position = pos.get()
				grenade_packet.velocity = (a,b,c)
				self.protocol.send_contained(grenade_packet)
			return connection.on_animation_update(self,jump,crouch,sneak,sprint)
   
	return protocol,GrenadeConnection

あと、これ見てわかるようにグレネード生成のときにはVertex3とかgrenade_packetとかいろいろインポートしなきゃならんので注意

何秒後に動作を起こす系

+ ...
callLater(時間, self.関数, 呼び出す関数の引数)

def _on_reload(self):
	if self.weapon == 0 or self.weapon == 1:
		callLater(0.01, self.reload_after)
	----略---
	self.send_contained(weapon_reload)
	return connection._on_reload(self)
これだとリロード時に武器が0または1(rifleまたはSMG)の時、リロードの0.01秒後に引数なしのreload_afterを実行

def flight(self, x, y, z):
	if self.flying:
		x2, y2, z2 = self.world_object.orientation.get()
		x += x2
		y += y2
		z += z2
		self.set_location((x, y, z))
		callLater(0.04, self.flight, x, y, z)
これは
 座標(x,y,z)を引数としてflight関数が読み込まれたとき
   flying が True であれば
     向いてる方向をゲットして、それぞれ三軸x2,y2,z2(-1から1まで)として
     各座標を方向の分だけずらして
     プレイヤーを新たなx,y,zにワープさせて
     0.04秒後に flight関数を x,y,zを引数として呼び起こす
 以下ループ
という意味



使うにはreactorのimportが必要
from twisted.internet.reactor import callLater


メッセージを表示する関数の使い方

+ ...
self.send_chat('message no naiyou')
が基本
変数を突っ込む場合はメッセージに%sを入れて、引用符閉じた後に% 変数名 を入れる
self.send_chat('You have %s medkit!' % self.medkits)
    →チャットの内容「You have 3 medkit!」

上記のだと赤い文字で個人にだけ送られるタイプのチャットになる。

青い文字で鯖からのメッセージとして表示する場合はselfとsend_chatの間にprotocolを入れて
self.protocol.send_chat("----")
とすればおk


selfとは、connectionとはなんぞや

+ ...
スクリプト眺めてるとめっちゃ出てくるこいつらは、変数や関数にくっついて「誰の」という部分を表す。

例えばこの例では(connection)が引数で出てる
コマンド打ってこの関数を呼び出した奴がconnectionになる。
だからconnection.medkits は「コマンド打ったプレイヤー」のmedkitsを表す変数になるし、connection.set_hp()はそいつのHPを変化させる関数になる

def medkit(connection):
    if connection.medkits and connection.hp < 100:
        connection.set_hp(connection.hp + connection.protocol.heal_amount,type = FALL_KILL)
        connection.medkits -= 1
        connection.send_chat('You have been healed')
    else:
        connection.send_chat("You don't have any medkits or have full health!")

こっちの例ではスポーンしたときに自動で呼び出される関数なのでselfになってる
つまりこの場合、self=スポーンしたプレイヤーとなる。
def on_spawn(self, pos):
	self.medkits = default_medkits
	self.send_chat('You have %s medkit!' % self.medkits)
	return connection.on_spawn(self, pos)
self.medkits = default_medkitsではスポーンしたひとのmedkit所持数にdefault_medkitsを代入している。
self.send_chat()ではselfの人にメッセージを送っている。

引数がconnectionの関数にselfをつけるとエラー吐くし、逆もだめなので引数を見て適当な方をつけましょう。


例えばあるプレイヤー"player"が味方か敵かを判定する場合
if player.team == connection.team:
とやるか、
if player.team == self.team:
とやるかを判断する場合は、そのdef____()のなかにselfがあるかconnectionがあるかを見て、それをつければいいのである。



こんな動作をさせたい!

+ ...

動作主の持ってる武器によって動作を変化

+ ...
	if self.tool == WEAPON_TOOL:
		if self.weapon == RIFLE_WEAPON:
			(ライフル動作)
		elif self.weapon == SMG_WEAPON:
			(SMG動作)
		elif self.weapon == SHOTGUN_WEAPON:
			(SG動作)



	if self.tool == WEAPON_TOOL:
は持ってるツールSPADE,BLOCK,WEAPON,GRENADEを判別

		if self.weapon == RIFLE_WEAPON:
は持ってる武器RIFLE,SMG,SHOTGUNを判別
ショットガンは"SG"じゃなくて"SHOTGUN"って書かないとダメ
全部大文字じゃないとダメ


スポーン時に何か動作をさせたい

+ ...
       def on_spawn(self,pos):
			(させたい動作)
          	return connection.on_spawn(self,pos)
スクリプトの中にこれがあればこの中に書き込む
無ければ書き足す(インデントの高さに注意)


       def on_spawn(self,pos):
は、スポーンするときに読み込まれる部分
selfはスポーンするプレイヤー、posはスポーン座標

細かいこと上の方の割り込みについての項目に詳しく書いてあるはず


HPを変化させたい

+ ...
				self.set_hp(50)
こう書く
selfの部分はconnectionとかhit_playerとかそのHPを変える対象によっていろいろ変化

変数値を突っ込む場合はfloat型じゃないとまずいようなので注意
よくわからない人はとりあえず変数を"float()"で包めばおk
				self.set_hp(float(x))


右クリでアイアンサイト覗いてるときになんかする系

+ ...
アイアンサイトなうの状況かどうかを判定するには
self.world_object.secondary_fire
を使います。ただこれだけだとSPADEで右クリ一気掘りとかしてる時にも判定されちゃう気がするので
if self.tool == WEAPON_TOOL and self.world_object.secondary_fire:
のようにするといいと思います。

右クリでスコープのぞいた瞬間になんかするなら
def on_secondary_fire_set(self,secondary):
を使うといいと思います。
これは右クリ押した瞬間に呼び出されるトリガーなので、解除のために右クリ押したときとか別の道具もってるときに右クリ押しても呼び出されてしまうので、いい感じにifとか使ってやるといいと思います。
なお、引数のsecondaryはスコープのぞいてる状態の時にTrueに、解除状態の時にFalseになります。


特定状況下では落下ダメを無効化したい

+ ...
       def on_fall(self, damage):
           if (状況):
               return False
           else:
               return connection.on_fall(self, damage)
落下時に呼び出されるやつにFalse返せば落下ダメは無視される。
引数damageは落下ダメージ量

プレイヤーの名前を取得したい、IDを取得したい

+ ...
名前はself.name
ID(#○○)はself.player_id
selfの部分は関数の呼び出した人が入る







意見や気になることがあったら書いてどうぞ
わからないことを書くと偉い人が加筆してくれるかもしれない
  • てすと -- あ (2016-08-20 14:48:36)
  • 右クリスコープで狙ってるときにVみたいな動作はどうやるの -- 名無しさん (2016-09-17 14:02:59)
  • ↑on_secondary_fire_set(self,secondary):で検出出来ると思う -- 名無しさん (2016-10-18 17:57:24)
  • ↑追記。connection.world_object.secondary_fireで静的にも状態を取得できます -- 名無しさん (2016-10-18 17:58:13)
名前:
コメント:

タグ:

+ タグ編集
  • タグ:

このサイトはreCAPTCHAによって保護されており、Googleの プライバシーポリシー利用規約 が適用されます。

最終更新:2016年12月09日 16:24