2025年5月6日にリリースされたGemini 2.5Pro Preview 05-06が良い。この一週間程度色々なプログラムを試しに書かせてみた。時々躓く部分はあるが、かなりの精度で期待通りのプログラムを作ることができている。
備忘録を兼ねて、Geminiを使ってSpreadsheetの内容からGoogle Slideを生成するプログラムを作る手順を残しておく。
“Spreadsheetの内容からGoogle Slideを生成するプログラムをGeminiに書かせる” の続きを読むスマホアプリとmacアプリ
2025年5月6日にリリースされたGemini 2.5Pro Preview 05-06が良い。この一週間程度色々なプログラムを試しに書かせてみた。時々躓く部分はあるが、かなりの精度で期待通りのプログラムを作ることができている。
備忘録を兼ねて、Geminiを使ってSpreadsheetの内容からGoogle Slideを生成するプログラムを作る手順を残しておく。
“Spreadsheetの内容からGoogle Slideを生成するプログラムをGeminiに書かせる” の続きを読むLLMの力を使ってコーディングの生産性をあげようという動きが活発だが、自分の中には「もはやコードは可能な限り書かない方が善だ」という感覚が生まれつつある。LLMができるタスクをわざわざ人の手を使って再生産したコードで動かすより、LLMに任せられる部分は可能な限りLLMに任せるようにしていきたい。LLMによって高速に書かれたコードであっても、コードとして存在する以上いつかは負債になるからだ。プロンプトももちろん負債にはなりうるが、自分が人間である限りコードよりはプロンプトの方がメンテナンスの難易度は低そうに思える。
まだまだ2025年現在ではLLMにはできないことも多いのでプログラムを自前で書かなければいけない部分もそこそこある。ただ、プログラムの一部分であってもLLMに頼れる部分は頼る、というスタイルは実験的にどんどん取り入れていきたいと思っている。
今回は、「与えられた数字が偶数かどうかを調べて返す」というあえてLLMにやらせなくても良いようなテーマを実際にGeminiを使って関数として実装してみる。
“プログラムの中でLLMに任せられる部分はなるべく丸投げしたい” の続きを読むgcloud auth application-default login
ローカルマシンでgcloud auth application-default login
コマンドを実行してアカウント認証を行うと、下記の場所にクレデンシャルファイルが格納される。
Mac: $HOME/.config/gcloud/application_default_credentials.json
Windows: %APPDATA%\gcloud\application_default_credentials.json
How Application Default Credentials works
上記のクレデンシャルファイルをdocker run
のバインドマウント機能を使ってコンテナにバインドする。
docker run -v ~/.config/gcloud/application_default_credentials.json:/tmp/keys/adc.json:ro
上の書き方であれば、Dockerコンテナ内で /tmp/keys/adc.json
というパスでローカルのGCPのApplication Defaultのクレデンシャルファイルを参照できるようになる。(Macの場合)
Docker Composeを使う場合は下記のように指定すればOK。
version: '3'
services:
app:
volumes:
- ~/.config/gcloud/application_default_credentials.json:/tmp/keys/adc.json:ro
GCPの各ライブラリは環境変数 GOOGLE_APPLICATION_CREDENTIALS
で指定された場所にあるクレデンシャルファイルを使って認証を行う。Dockerコンテナにマウントしたクレデンシャルの保存先 /tmp/keys/adc.json
を GOOGLE_APPLICATION_CREDENTIALS
にセットするように設定すれば、Dockerコンテナ内でローカル環境の認証情報を使ってGCPの各ライブラリを動かすことができるようになる。
version: '3'
services:
app:
environment:
GOOGLE_APPLICATION_CREDENTIALS: /tmp/keys/adc.json
volumes:
- ~/.config/gcloud/application_default_credentials.json:/tmp/keys/adc.json:ro
これまでARアプリを作るときはARKitを使うことがほとんどだったが、マルチプラットフォームで動くライトウェイトなARアプリを作る機会があり、Web ARを試すことになった。Web ARを実現するためのライブラリは多数存在するが、2024年現在もっとも多く使われているライブラリは調べる限りAR.jsのようだ。サポートしているブラウザの種類や資料の数をみても、今の所AR.jsを使わない理由はなかったのでAR.jsを試してみることにした。
“Web AR開発用のJSライブラリ AR.js” の続きを読むGoogle Apps Scriptに用意されているSlidesサービスを使うと、プログラムでスライドを新しく生成することができる。
https://developers.google.com/apps-script/reference/slides/slides-app
SlideApp
クラスのcreate
メソッドを使うと全く新しいスライドを新規で生成できるが、まっさらなスライドに要素を追加していく処理を全てスクリプトで書くのは結構大変なので、ベースとなるスライドをコピーするアプローチをとった方が何かと楽だ。
// srcFileId: コピー元のスライドのID
// filename: コピーして作られたファイルにつけるファイル名
const copy = DriveApp.getFileById(srcFileId).makeCopy(filename);
// 特定のフォルダに配置したければmoveToを呼び出す
// copy.moveTo(DriveApp.getFolderById(targetFolderId));
const newSlideId = copy.getId();
ファイルのコピーはSlidesサービスではなくDriveサービスのmakeCopyで行う。
const slide = SlidesApp.openById(newSlideId);
コピーしたスライドのIDをSlidesAppのopenByIdの引数に渡す形で、スライドを開き編集していくことができる。
const firstSlide = slide.getSlides()[0]
firstSlide.insertTextBox('Hello');
Google Apps ScriptからBigQueryの機能を利用するにはAdvanced ServiceであるBigQuery Serviceを利用する。
https://developers.google.com/apps-script/reference#advanced_services
このAdvanced Serviceのメソッドを調べるには少し知識が必要になる。
“Google Apps ScriptのBigQuery Serviceのメソッドの引数の調べ方” の続きを読むApps Scriptでスプレッドシートをテーブル的に使いたい時に、シートの内容をオブジェクトの配列として取得したいケースがよくある。
シート全体のセルの値はgetValues()
もしくはgetDisplayValues()
メソッドを使って取得することができる。
取得した値のうち、一行目をヘッダー行(keyの配列)として取り出し、残りの行を値として取り出すことで、オブジェクトの配列を作る。
function getRowsAsObjects(sheet) {
var data = sheet.getDataRange().getDisplayValues();
const header = data.shift();
var rows = data.map(function(row) {
var dic = {}
header.forEach((h, idx) => {
dic[h] = row[idx];
});
return dic;
});
return rows;
}
すると、このような結果が得られる。
[{name=Apple, date=2024/01/01}, {date=2024-03-13, name=Orange}]
よく書くコードなので、GASのライブラリとしてまとめた。
https://script.google.com/home/projects/1igMdPkdNu4Yg7u_rnBrXYTh7x15KotD0IpikNvrP52fCLV67ClXJ81ut
スクリプトID: 1igMdPkdNu4Yg7u_rnBrXYTh7x15KotD0IpikNvrP52fCLV67ClXJ81ut
使い方
var objects = SheetValues.getRowsAsObjects(SpreadsheetApp.getActiveSpreadsheet().getSheetByName("TestSheet"))
Godot 4で、フォルダ内にあるtresファイルを動的に読み込むプログラムを書いたところうまく動作しなかった。
for file in dir_contents(folder_path):
if file.get_extension() == "tres":
tres_files.append(file)
上のコードは、特定のフォルダにあるtresファイルを全てリストアップするためのコードだ。Godot Editor上でこのコードを実行すると、フォルダ内のtresファイルがリストアップされるが、iOS上ではファイルが一つもリストアップされない。調べてみると、.tresファイルは見つからないが代わりに.tres.remapというファイルが存在していることがわかった。
https://github.com/godotengine/godot/issues/66014
Godot 3では動作していたのだが、どうやら仕様が変わりtresファイルもRemapされるようになったらしい。Remapされたファイルはload()やpreload()でres://プロトコルでファイルを指定して呼び出す際には内部的に自動でRemap後のデータが参照されるため、プログラム内で参照先をハードコーディングしているような場合は特にファイルの実態を意識する必要がないが、今回のように「フォルダからファイルを探して動的に読み込む」というような実装では「フォルダ上に見つかるファイル(remapファイル)」と「res://プロトコルで参照する際のURL」に差異が生まれてしまうのでそのままでは動かなくなってしまう。
for file in dir_contents(folder_path):
if file.get_extension() == "tres":
tres_files.append(file)
if '.tres.remap' in file: # 追加
image_files.append(file.trim_suffix(".remap"))
Godot 4でそのようなことをやりたい場合は、フォルダ内から.tres.remap
ファイルを探し、読み込む際には.remap
を取り除いたURLでload()
するように実装する。ただし、Godot Editor上で作業している際にはRemapが行われていない元ファイルを参照しに行くことになるので、上のコードのように.tres
ファイルと.tres.remap
ファイルの両方に対応できるようなコードを書く必要がある。
https://github.com/godotengine/godot/issues/25672
最初にはったIssueと、上に貼ったもう一つのIssueでも指摘されているが、なかなかわかりにくい仕様だと思う。
2020年末に購入したM1チップ搭載のMacBook Proが快適だ。Xcodeを使ったアプリ開発やPremiereでの動画編集はもちろん、最近少しずつ増えてきたニューラルネットワークが内部的に使われているようなAIアプリのパフォーマンスにおいて、Intel搭載のiMacとは顕著な差があることを実感している。
早くiMacもApple Silicon搭載の27インチモデルが発売されないかとずっと待ってきたが、待ち続けているうちに2年が経過してしまった。2023年中には発売されるのではないかという噂もあったが、昨今のApple製品の値段設定を見ていると発売されてもかなり高価になりそうなこと、それを待つ間の作業今のiMacの作業効率で失う生産性のバランスを考えて、24インチモデルの現行iMacに買い換えることにした。
Apple Siliconが出た当初はDockerや音源プラグインなどで色々問題があったが、最近ではほとんど解消され、さらにはプログラムの最適化も進んでおり環境構築もかなりスムーズに終わった。環境が綺麗な状態で、パフォーマンスをテストしておこうと思う。
“M1 iMacへの買い換え” の続きを読むGodotではAudioStreamPlayerというNodeを使うと簡単にサウンドを再生させることができる。GameStageシーンにAudioStreamPlayerを追加する。
Nodeの名前はHitSoundにした。
FinderからFileSystem dockに適当なサウンドファイルを読み込む。
HitSoundのInspectorで、Streamプロパティのところに先ほど読み込んだサウンドファイルを指定する。
func _input(event):
if event is InputEventMouseButton and event.button_index == BUTTON_LEFT and event.pressed:
for enemy in get_tree().get_nodes_in_group("enemies"):
var distance = event.position.distance_to(enemy.position)
if distance <= ENEMY_RADIUS:
enemy.position = Vector2(rng.randf_range(-STAGE_MARGIN, get_viewport().size.x + STAGE_MARGIN), -STAGE_MARGIN)
score += 1
get_node("HitSound").play(0)
サウンドを再生したいタイミングで、play(0)を呼ぶとそのサウンドが冒頭から再生される。今回のコードでは、標的が叩かれたタイミングでplay(0)を呼び出して効果音が鳴るようにした。
パーティクルシステムを使って、標的の軌跡を描いてみる。
各標的から軌跡のパーティクルが放出されるようにしたいので、Hae.tscnにパーティクルシステムを導入する。Godotのパーティクルシステムには2種類のパーティクルシステムが用意されているが、Open GL ES 3系のプロジェクトでは、より高度でGPUで処理可能なParticles2Dノードを使うことができるので、今回はParticle2Dを追加。
Particles2Dノードが追加されたが、動作に必要なMaterialがセットされていないので警告マークが付いている。
ParticleのProcess Material(描画される一つ一つのパーティクルの表現を決めるもの)がemptyになっているので、New ParticlesMaterialを選択して新しいMaterialを作る。Inspectorの下の方にCanvas ItemのMaterialを設定する項目があるが、こちらではないので注意。
次にTexture(パーティクルの描画に使われる画像)を指定する。適当な画像をFileSystemに読み込んで、そこからドラッグで指定できる。
エディタ上でパーティクルシステムの動きが既に確認できるようになっている。現在は縦方向にパーティクルが放出されるだけであまりかっこよくないので、放射状に広がるようにパラメータを調整したい。
このパーティクルの動きは先ほど作ったMaterialの設定から行う。現在パーティクルが放出されると下に落ちていくのはGravityの影響なので、一旦Gravitiyを全て0にセットする。こうすることでパーティクルは生成されても下に落ちて行かなくなる。次にInitial Velocityに適当な値をセットする。この値はパーティクルが放出されるときの初速となり、大きくすれば大きくするほど勢いよくパーティクルが放出されるようになる。奇跡として描く場合はあまり大きくしすぎないほうが良いだろう。最後にDirectionで放出される方向を設定する。今回は特定の方向ではなく全方位に放出されて欲しいので、xyzはそれぞれ0をセットし、放出の範囲を決めるSpreadに180をセットした。
若干面倒ではあるが、Color Rampのところでグラデーションを指定することで、フェードインやフェードアウトも表現することができる。
設定の変更はすぐにエディタ上にも反映され動作を確認することができる。必要に応じて、その他のパラメータを調整して良い感じになるようにする。
最後に、Particles2DノードのDrawing / Local CoordsプロパティをOffにする。これをOnにしていると、放出されたパーティクルは親のNodeの移動に合わせて一緒に移動してしまう。今回は軌跡として表示をしたいので、標的(親ノード)が移動したとしても放出されたパーティクルは放出された場所にとどまって欲しいので、Offにする。
一週間あるからある程度形になるだろうと思ってGodotを始めてみたが、前半油断してゆっくりやりすぎたこと、そしてそもそも実は休みが六日間しかなかったことなどもあって後半かなり駆け足になってしまった。とりあえずGodotを使ってゲームを作っていく上での基礎的なことは分かってきた感じがある。Godotはかなりサクサク動くし、Pythonライクな文法のGDScriptもコーディング量が少なく、気軽に遊びには良いゲームエンジンだなと思った。
Godotはクロスプラットフォームであるという点が魅力の一つなので、もうちょっとゲームとして形になったらiOS / Androidでそれぞれ動かしてみるというのを試してみたいと思う。