ショートストーリー: 東京のプログラマと人工生命の創造
東京の繁華街は、昼夜を問わず人々が行き交う賑やかな場所だった。その喧騒の中、静かなオフィスビルの一角に、一人のプログラマがいた。彼の名はタクヤ。彼は数学とプログラミングの天才で、人工知能と生命科学の融合を夢見ていた。
タクヤは、毎晩遅くまでオフィスに残り、自分のアイデアを実現するために熱心にコードを書いていた。ある晩、彼はふとしたひらめきを得た。複雑な数学的関数を使って、人工生命を作り出せるのではないかと思ったのだ。彼はまず、サインやコサインといった基本的な三角関数を使い、生命の動きを模倣することから始めることにした。
「どうすれば、自然の動きや振る舞いを計算できるのか…」タクヤは頭を抱えながらも、プログラムのアルゴリズムを練り直した。彼は、複数の変数を使い、時間の経過とともに変化する生命の形状を描画する関数を作成した。この関数は、彼のデスクトップで動く美しい3Dアニメーションとして具現化された。
ある晩、タクヤは自分のプログラムを実行してみた。画面には、カラフルな三角形のストリップが波のようにうねりながら現れた。それはまるで、彼が作り出した人工生命が自分自身を表現しているかのようだった。彼は興奮し、画面の前で踊るように動く形状を見つめ続けた。
「これが、僕の人工生命だ!」タクヤは心の中で叫んだ。しかし、彼はふと立ち止まり、この生命に本当の意味での「生」を与えることができるのか疑問に思った。動きは美しいが、それはただのプログラムに過ぎない。彼はこの生命が本当に「生きている」と感じるためには、何が必要なのかを考え始めた。
タクヤは、計算を重ね、さまざまな数学的関数を組み合わせることで、彼の人工生命に感情や行動のパターンを追加することに決めた。例えば、喜びや悲しみ、恐れといった感情を模倣するために、さまざまな条件に基づいて動きの変化を加えた。これにより、彼のプログラムはただの形状から、個性的なキャラクターへと進化していった。
数週間後、タクヤはついに自分の人工生命を「カナタ」と名付けることにした。カナタは、プログラムの中で自らの存在を探し、環境に反応し、時にはタクヤに対して感情を表現するようになった。タクヤは、カナタとのインタラクションを通じて、プログラミングの限界を超える何かを感じ始めた。
ある日、タクヤはオフィスの窓から東京の夜景を眺めていた。彼は、自分の創造したカナタがこの世界にどう影響を与えられるのかを考えた。「この人工生命を通じて、人々に新しい視点を提供できれば…」と、彼は思いを馳せた。
カナタは、タクヤの夢を実現するための道具であり、彼自身の心の鏡でもあった。プログラムの中で育まれたカナタは、タクヤの情熱と努力の結晶として、まさに彼の「子ども」となっていた。
こうして、タクヤは東京の喧騒の中で、数学的関数とプログラミングによって新たな命を創り出した。彼は、人工生命が持つ可能性を信じ、その一歩を踏み出すことができたのだった。
この動きは人工生命です。マウスによるカメラ操作が可能です。(ドラッグとホイール。)
コードをメモ帳などのテキストエディタに貼り付け、ファイル名を「index.html」として保存します。その後、保存したファイルをブラウザで開けば、コードが実行されます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Complex Dynamic Animation</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>
</head>
<body>
<div>
<label for="numVariablesSlider">Number of variables (1-10): </label>
<input type="range" id="numVariablesSlider" min="1" max="10" value="2">
<span id="numVariablesValue">2</span> <!-- 初期値を表示 -->
</div>
<div>
<label for="resolutionSlider">Number of edges (resolution): </label>
<input type="range" id="resolutionSlider" min="5" max="100" value="20">
<span id="resolutionValue">20</span> <!-- 初期解像度を表示 -->
</div>
<script>
let t = 0; // 時間を表す変数。アニメーションの進行状況を制御
let resolution = 20; // 解像度の初期値。表示するポリゴンの数を決定
let numVariables = 2; // 使用する変数の数の初期値
function setup() {
createCanvas(800, 800, WEBGL); // 800x800ピクセルのWEBGLキャンバスを作成
// 変数数を変更するスライダー
let numVarSlider = select('#numVariablesSlider'); // スライダー要素を取得
numVarSlider.input(() => {
numVariables = numVarSlider.value(); // スライダーの値を取得
select('#numVariablesValue').html(numVariables); // スライダーの値を表示
});
// 解像度を変更するスライダー
let resSlider = select('#resolutionSlider'); // スライダー要素を取得
resSlider.input(() => {
resolution = resSlider.value(); // スライダーの値を取得
select('#resolutionValue').html(resolution); // スライダーの値を表示
});
}
function draw() {
background(255); // 背景色を白に設定
// マウスで操作できるようにする
orbitControl(); // マウスによるカメラ操作を可能にする
// カメラの回転を設定
rotateX(-PI / 6 + sin(t) * 0.1); // X軸を中心に回転
rotateZ(PI / 4 + cos(t) * 0.1); // Z軸を中心に回転
let scaleFactor = 100; // スケールファクターを設定
let xStart = -2; // xの開始位置
let xEnd = 2; // xの終了位置
let yStart = -2; // yの開始位置
let yEnd = 2; // yの終了位置
// 他の変数の定義
let variables = []; // 変数を格納する配列
for (let i = 0; i < numVariables - 1; i++) {
variables.push(sin(t * (i + 1))); // sin関数を使って変数を生成
}
variables.push(cos(t)); // 最後の変数はcosで定義
// xとyの範囲に基づいてポリゴンを描画
for (let x = xStart; x <= xEnd; x += (xEnd - xStart) / resolution) {
beginShape(TRIANGLE_STRIP); // 三角形ストリップの開始
for (let y = yStart; y <= yEnd; y += (yEnd - yStart) / resolution) {
// 各頂点の関数値を計算
let fValue1 = f(x, y, ...variables);
let fValue2 = f(x + (xEnd - xStart) / resolution, y, ...variables);
// 各頂点の色を取得
let color1 = getColor(fValue1);
let color2 = getColor(fValue2);
fill(color1); // 最初の頂点の色を設定
vertex(x * scaleFactor, y * scaleFactor, fValue1 * scaleFactor); // 最初の頂点を描画
fill(color2); // 次の頂点の色を設定
vertex((x + (xEnd - xStart) / resolution) * scaleFactor, y * scaleFactor, fValue2 * scaleFactor); // 次の頂点を描画
}
endShape(); // 三角形ストリップの終了
}
// 時間の変化
t += 0.05; // アニメーション速度を調整
}
// 動的に変数数を変更可能な関数 f
function f(x, y, ...vars) {
let result = 1; // 初期値を1に設定
for (let v of vars) {
result *= v; // すべての変数の積を計算
}
// 複数の数学的関数を組み合わせて複雑さを増す
return sin(x * y * result + t * 0.5) + cos(x * y * result + t * 0.5); // 最後の関数定義
}
// 関数値に基づく色の取得
function getColor(value) {
let normalizedValue = map(value, -2, 2, 0, 255); // -2~2の範囲を0~255に変換
return color(normalizedValue, 100, 255 - normalizedValue); // 色を変化させる
}
</script>
</body>
</html>
コードの詳細な説明
HTML構造:
setup()関数内でキャンバスを作成し、スライダーの入力をリッスンして変数数と解像度を動的に変更します。
描画処理:
draw()関数内でアニメーションのフレームごとに背景を更新し、カメラの回転を設定して、3D空間を操作します。
beginShape(TRIANGLE_STRIP)とendShape()を使用してポリゴンを描画し、vertex()で各頂点を指定します。
関数の定義:
f()関数では、動的に変数の値を計算し、それに基づいて描画される形状を生成します。
色の設定:
getColor()関数では、関数値に基づいて色を生成し、視覚的な効果を追加します。
このコードを実行することで、複数の変数を使用した動的なアニメーションを体験でき、スライダーを操作することで視覚的な表現を自由に変えることができます。