pirika logo

ホームページ Pirikaで化学 ブログ 業務案内 お問い合わせ
情報化学+教育トップ 情報化学 MAGICIAN MOOC プログラミング
高校生レベルのプログラミングはMAGICIAN-Jrへどうぞ。
フィードバックはお問い合わせフォームからお願いします。

プログラミング・トップ > 化学,薬学系の親子で楽しみながらプログラミング > 第5時限:理科

2022.3.3

Pirika.comでプログラミング
山本博志

5-4:何故、炭酸ガスだけが悪者?窒素や酸素は?

去年のノーベル賞で、真鍋先生が気候変動のシミュレーションでノーベル賞を取りました。

ですから、炭酸ガス(CO2)が悪者なのは皆さんも知っているでしょう。
では、何故、窒素(N2)や酸素(O2)は悪者ではないのか知っていますか?

空気の中には窒素は78%、酸素(21%)と大量にあるし、二酸化炭素は、たった0.04%しかないのです。

答えは、「炭酸ガスは地球温暖化係数が窒素や酸素よりも何万倍も高いから」。

納得できますか?

これが、理科をつまらなくする最大の理由です。

これでは、何故?に答えたことにはならないのです。

少ない量なのに温暖化に激しく影響を与えているので、窒素や酸素のXXXXX倍って決めたのです。

「何故、勉強のできないの?」って子供に聞いて、「だってテストの点が悪かったんだもの」で納得するか、と同じです。

さー、そこで、真鍋先生がおっしゃっている、”もっと楽しむ姿勢で”、何故かを考えるシミュレーションを考えてみましょう。

真鍋先生が当時使っていたコンピュータは、今のスマホよりも全然遅いものでした。

今なら、とても簡単に試す事ができます。

このプログラムは、次の所から持ってきています

これを元に、いろいろ改良していきます。
ここではまず綺麗なアニメーションを楽しみましょう。

bouncing balls

Startボタンを押して見てください。

空気中の分子、原子はこのように飛び回っています。大きい分子はゆっくり、小さい分子は早く動いています。
壁とぶつかって跳ね返ったり、途中で分子同士がぶつかったりします。
色は全く意味がありません。ランダムにつけています。

ただ、これを気体分子の運動として見ているのは、化学者だけかもしれません。
元々は単なるHTML5のキャンバス利用方法の綺麗なデモです。

壁に衝突して反対側に進むときには、作用・反作用の力が働きます。ボールが反対側に跳ね返るときに、壁にも同じ大きさの反対向きの力が働きます。
この、壁にかかる力をボールごとに全部足してあげると、気体の持っている圧力になります。
低い圧力(低気圧)が来ると天気が悪くなり、高気圧が来ると天気が良くなる、その気(体の)圧(力)です。

プログラムのコピペ

次のプログラムをコピペしてBoundingBall.htmlという名前でセーブしましょう。

完成形プログラム(▶︎をクリックして開く)

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <title>跳ね返るボール</title>
  </head>
  <body>
  <h1>bouncing balls</h1>
<canvas width="600" height="350"></canvas>
<input type="button" id="btnStart" value="Start" onclick="StartSimu()">
 
<script type="text/javascript">
// set up canvas

const canvas = document.querySelector('canvas');
const ctx = canvas.getContext('2d');

const width = canvas.width ;//= window.innerWidth;
const height = canvas.height ;//= window.innerHeight;

let MySimu = false;      // シミュレーション・フラグ

// function to generate random number

function random(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}

// function to generate random RGB color value

function randomRGB() {
  return `rgb(${random(0, 255)},${random(0, 255)},${random(0, 255)})`;
}

class Ball {

   constructor(x, y, velX, velY, color, size) {
      this.x = x;
      this.y = y;
      this.velX = velX;
      this.velY = velY;
      this.color = color;
      this.size = size;
   }

   draw() {
      ctx.beginPath();
      ctx.fillStyle = this.color;
      ctx.arc(this.x, this.y, this.size, 0, 2 * Math.PI);
      ctx.fill();
   }

   update() {
      if ((this.x + this.size) >= width) {
         this.velX = -(this.velX);
      }

      if ((this.x - this.size) <= 0) {
         this.velX = -(this.velX);
      }

      if ((this.y + this.size) >= height) {
         this.velY = -(this.velY);
      }

      if ((this.y - this.size) <= 0) {
         this.velY = -(this.velY);
      }

      this.x += this.velX;
      this.y += this.velY;
   }

   collisionDetect() {
      for (const ball of balls) {
         if (!(this === ball)) {
            const dx = this.x - ball.x;
            const dy = this.y - ball.y;
            const distance = Math.sqrt(dx * dx + dy * dy);

            if (distance < this.size + ball.size) {
              ball.color = this.color = randomRGB();
            }
         }
      }
   }

}

const balls = [];

while (balls.length < 25) {
   const size = random(10,20);
   const ball = new Ball(
      // ball position always drawn at least one ball width
      // away from the edge of the canvas, to avoid drawing errors
      random(0 + size,width - size),
      random(0 + size,height - size),
      random(-7,7),
      random(-7,7),
      randomRGB(),
      size
   );

  balls.push(ball);
}

function loop() {
    if(MySimu){
   ctx.fillStyle = 'rgba(0, 0, 0, 0.25)';
   ctx.fillRect(0, 0,  width, height);

   for (const ball of balls) {
     ball.draw();
     ball.update();
     ball.collisionDetect();
   }

   requestAnimationFrame(loop);
   }
}

// 開始ボタンが押されたとき
function StartSimu() {
  if (MySimu) {
    MySimu = false;
    document.getElementById('btnStart').value = 'シミュレーション開始';
  } else {
    MySimu = true;
    document.getElementById('btnStart').value = 'シミュレーション停止';
    loop();
  }
}
   
   
 </script>
  </body>
</html>

簡単に説明します


Ballを描く位置、動く方向と速さ、色、大きさを決めてあげます。

      this.x = x;
      this.y = y;
      this.velX = velX;
      this.velY = velY;
      this.color = color;
      this.size = size;

残像を残して消す。

これはR, G, Bの値は0が入っているので黒色です。
全部消してしまうと何も残らないのですが、前の画像に透明度(0.25)で黒く上書きします。
セルをのせるようなイメージです。

そしてボールが移動したところを書く。また黒い半透明のセルをのせる。そうすると、一番下にあるものほど薄くなっていくので残像に見えます。

これが、このプログラムのキモと言うべき面白いテクニックです。

ctx.fillStyle = 'rgba(0, 0, 0, 0.25)';
ctx.fillRect(0, 0,  width, height);

開始ボタンと停止ボタンをつける。

これは元のプログラムにはありませんでした。
MySimuと言うグローバル変数(プログラムのどこでも使える)を定義します。
そしてこの変数はBoolean型(ブーリアン型は、真理値の「真 = true」と「偽 = false」という2値をとるデータ型)と言う変な変数になります。
Startボタンが押されるたびにtrueとfalseの値が入れ変わります。

trueの時にはシミュレーションが継続し、falseの時には一旦停止します。
if命令の( )の中にはtrueかfalseが入ります。そしてtrueの時には最初の{ }の中が実行され、falseの時には else{ }の中が実行されます。

let MySimu = false;      // シミュレーション・フラグ

// 開始ボタンが押されたとき
function StartSimu() {
  if (MySimu) {
    MySimu = false;
    document.getElementById('btnStart').value = 'シミュレーション開始';
    //document.getElementById('btnStep').disabled = false;
  } else {
    MySimu = true;
    document.getElementById('btnStart').value = 'シミュレーション停止';
    //document.getElementById('btnStep').disabled = true;
    loop();
  }

ボールを描く

このプログラムでは、とても高度なテクニックを使っています。
ここでは25個のボールを扱います。
そして各ボールは、xの位置、yの位置、x方向の速度、y方向の速度、色、大きさの情報を持ちます。
それを一つ一つのボールについて定義して、動作を記述しようとすると大変なことになります。
そこで、ここではボールのクラスというものを定義しています。
これは、ボールのデータと動作をひとまとめにしたものです。
そのひとまとめが25個あるという考え方をします。

class Ball {

    constructor(x, y, velX, velY, color, size) {//ボールのデータ
    
    draw() {//ボールを描く

    update() {//壁での反射
    
    collisionDetect() {//衝突判定
    
}

データと機能をカプセル化という高度なプログラムになっています。

各ボールに、ball.draw()という命令を送れば、自分の位置や速度、色、サイズはボール自身がデータとしてわかっているので、勝手に描いてくれます。
壁での反射や衝突判定も各ボールに自分で判定させます。

全部の動きを人間がプログラムしてコントロールするのではなくて、自律的に動いています。

次回で、いろいろ改良していきます。

プログラミング・トップ > 化学,薬学系の親子で楽しみながらプログラミング


Copyright pirika.com since 1999-
Mail: yamahiroXpirika.com (Xを@に置き換えてください) メールの件名は[pirika]で始めてください。