Lua例文集

Last-modified: 2025-11-10 (月) 20:01:26

はじめに

やりたいことがあるけどこれってどうすればいいの?という人向けの例文集。
1%のひらめきをあなたに
追加募集中!

Logic再現系

Logicブロックの機能をLuaで再現するコード

pulse

1tick前の入力Bool値と比較することで入力値の変化した1tickだけtrueを返す。

 prev_tick = false
 function onTick()
    value = input.getBool(1)
    pulse = not (value == prev_tick)
    output.setBool(1, pulse)
    prev_tick=value
 end

toggle

一つ上のpulseを利用し、保存値を反転させる。
※Lua内部の変数はマルチで同期されないため、マルチでは非同期バグが発生する可能性が非常に高いので要注意

 prev_tick = false
 function onTick()
    value = input.getBool(1)
    pulse = not (value == prev_tick)
    output.setBool(1, pulse)
    prev_tick = value
 if pulse then
    toggle = not toggle
 end
 end

timer

用途によって色々な方法がある。しかし例によって非同期バグの可能性があるため注意。
tickごとに増加する数値を用いて一定値以上でON/OFFを出力する

 time = 0
 tick = 120
 function onTick()
    time = time + 1
    TON = time > tick
    TOFF = time < tick
 end

clamp

数値に制限を掛けたいときに使用

 value = math.max(math.min(value,MAX),MIN)

MIN < value < MAXとなる

PID

個人的に作ったものなので挙動がおかしくなる可能性があります
修正大歓迎
ゲイン調整などはこちら

local PID = {
   new = function(kp, ki, kd, dt)
       local self = {
           kp = kp,
           ki = ki,
           kd = kd,
           dt = dt,
           prev_error = 0,
           integral = 0,
           output = 0,
       }
       function self:update(target, current)
           local error = target - current
           local derivative = (error - self.prev_error) / self.dt
           self.integral = self.integral + error * self.dt
           self.output = self.kp * error + self.ki * self.integral + self.kd * derivative
           self.prev_error = error
           return self.output
       end
       return self
   end
}
-- Example
local Gain_P = 1
local Gain_I = 0
local Gain_D = 1
local Gain_dt = 1
local SetPoint = 0  --目標値
local ProcessVariable = 1  --現在値
local PID_Control = PID.new(Gain_P,Gain_I,Gain_D,Gain_dt)
local Output = PID_Control:update(SetPoint,ProcessVariable)

文字数削減系

スペース消し

余計なスペースを消すだけだが必要なスペースは残すこと。
左下のボタンで構文チェックができるので活用しよう。

入出力系の短縮

クラス名を短縮できる。入出力系は非常に文字数がかさむので短縮の効果がかなり高い。ついでにコードの打ち込みの時間も短縮できる。
大体3回以上使うならば圧縮した方が得である。

 i,o=input,output
 gn,gb,sn,sb=i.getNumber,i.getBool,o.setNumber,o.setBool
 function onTick()
    A=gn(1)
    B=input.getNumber(1)
 end

実行するとA=Bとなる。

mathの短縮

同上。

 m=math
 sin,cos,abs,pi=m.sin,m.cos,m.abs,m.pi
 function onTick()
    A=sin(pi)
    B=math.sin(math.pi)
 end

実行するとA=Bとなる。

クラス短縮系

同様に、property、screen、string、mapなども短縮できる。てかできないことある?

三項演算子もどき

Bool値のTrue/Falseで数値を切り替えたいとき、if文よりも短く書ける。

 A=true
 num1=100
 num2=200
 B=A and num1 or num2

A=trueのときB=num1、falseのときB=num2となる

変数名の短縮

様々な観点からあまりよろしくはないが文字数を節約できる。
可読性を無視するなら変数を出現頻度順に並び変えて頻度順にa,b,c,d,...,aa,ab,ac,...と置き換えておけばよい。Luaブロックのレイテンシの為に1つに収めたいなどよっぽどのことがない限りやめた方がいい。

  • ある程度可読性を維持するために
    どこかに変数名と機能(中身)をコメントアウトしておく
     a=1--エンジン用PIDのPゲイン
     b=0.1--Iゲイン
     c=2--Dゲイン
    機能(中身)の頭文字を使う
     ER=input.getNumber(1)--Engine RPS
     FT=AT*0.5 --Fuel manuhold Throttle
    番号を付ける
     ER1=input.getNumber(1)--1st Engine RPS
     ER2=input.getNumber(2)--2nd Engine RPS

for文

同じ動作を複数行う場合に有効。Tableと組み合わせるとさらに強力。

 Table.Bool = {}
 Table.Number = {}
 function onTick()
    for i=1,32 do
       Table.Bool[i]=input.getBool(i)
       Table.Number[i]=input.getNumber(i)
    end
    --何らかの処理
    for i=1,32 do
       output.setBool(i,Table.Bool[i])
       output.setNumber(i,Table.Number[i])
    end
 end

これで入出力が完結する。上と組み合わせるとさらに文字数を圧縮できる

行列演算

StormWorks Luaには行列(テーブル)の演算が実装されていないため、自力で実装する必要がある。おのメタ
※作成中

行列の乗算 (A * B)
A: m x n, B: n x p
戻り値: m x p の行列

 function multiply(A, B)
   local m = #A
   local n = #A[1]
   local p = #B[1]
   -- Bの列数がAの行数と一致するか (n == #B)
   if n ~= #B then
       -- なんらかのエラー処理
   end
   local C = {}
   for i = 1, m do
       C[i] = {}
       for j = 1, p do
           local sum = 0
           for k = 1, n do
               sum = sum + A[i][k] * B[k][j]
           end
           C[i][j] = sum
       end
   end
   return C
 end

転置

行列の転置 (A^T)
A: m x n の行列 (テーブルのテーブル)
戻り値: n x m の行列

local function transpose(A)
   local m = #A
   local n = #A[1]
   local At = {}
   for j = 1, n do
       At[j] = {}
       for i = 1, m do
           At[j][i] = A[i][j]
       end
   end
   return At
end

逆行列

2x2行列の逆行列
M: {{a, b}, {c, d}}
戻り値: M^-1

 local function invert2x2(M)
   local a = M[1][1]
   local b = M[1][2]
   local c = M[2][1]
   local d = M[2][2]
   -- 行列式 (Determinant)
   local det = a * d - b * c
   if math.abs(det) < 1e-10 then
       -- 行列式がほぼ0の場合、逆行列は存在しない(または不安定)
       -- なんらかのエラー処理
   end
   local invDet = 1.0 / det
   local M_inv = {
       { invDet * d,  invDet * -b },
       { invDet * -c, invDet * a  }
   }
   return M_inv
 end

スカラー倍

要素積

べき乗

その他便利系

フィジックスセンサーのオイラー角から自機の府仰角、方位角、回転角を求める方法

回転行列を作り、計算する

 -- フィジックスセンサーからオイラー角を取得
 local euler_x = input.getNumber(4)
 local euler_y = input.getNumber(5)
 local euler_z = input.getNumber(6)
 -- 各軸周りの回転に対するsinとcosを計算
 local cos_x = math.cos(euler_x)
 local sin_x = math.sin(euler_x)
 local cos_y = math.cos(euler_y)
 local sin_y = math.sin(euler_y)
 local cos_z = math.cos(euler_z)
 local sin_z = math.sin(euler_z)
 -- 回転行列 (Rotation Matrix) を計算
 local RotationMatrix = {
     {
         cos_y * cos_z,
         sin_x * sin_y * cos_z - cos_x * sin_z,
         cos_x * sin_y * cos_z + sin_x * sin_z
     },
     {
         cos_y * sin_z,
         sin_x * sin_y * sin_z + cos_x * cos_z,
         cos_x * sin_y * sin_z - sin_x * cos_z
     },
     {
         -sin_y,
         sin_x * cos_y,
         cos_x * cos_y
     }
 }
 -- 回転行列から各角度を抽出
 -- 仰角
 local Elevation = math.asin(RotationMatrix[2][3])
 -- 方位角
 local Azimuth = math.atan(RotationMatrix[1][3], RotationMatrix[3][3])
 -- 回転角
 local Roll = math.atan(RotationMatrix[1][2], RotationMatrix[2][2])

Switch

C++におけるswitch-case、Pythonでいうところのmatch-caseです。階層的な処理をifよりも比較的きれいに書けます。

--本体
local function switch(value)
   return function(cases)
       local case = cases[value] or cases.default
       if case then
           return case(value)
       else
           error(string.format("Invalid case (%s)", value), 2)
       end
   end
end
--例
switch(Mode) {
   [1] = function()
       --Modeが1の場合の処理
   end,
   [2] = function()
       --Modeが2の場合の処理
   end,
   [3] = function()
       --Modeが3の場合の処理
   end,
   ["A"] = function()
       --Modeが"A"の場合の処理
   end,
   ["B"] = function()
       --Modeが"B"の場合の処理
   end,
   ["C"] = function()
       --Modeが"C"の場合の処理
   end,
   default = function()
       --Modeが上記のどれにも当てはまらない場合の処理
   end
}
switch(1) --1の処理が実行される
switch("A") --"A"の処理が実行される