名前付けはとっても大事!:一年前のコードを見直してみよう
プログラミングにおいて名前付けってとっても大事だし、難しいものでもあります。
その名前付けを意識して以前に書いた Processing のコード、書きながら自分自身で疑問点がいろいろと湧いてきていました。
それから一年以上経過して自分が成長したのか見てみたいと思い、疑問点と合わせてコードを見直してみました。
細かいところを見直して…と思ったけど
元のコードはこういうアニメーションを表示するものなのですが…
…ダメだこりゃ。😱
コードを読み直してみるととても読みづらいし、何をやりたいのか全然わからない…
私はもう以前の私じゃないの…
まず、draw() の中がひどい。
float orbitRadius = frameCount * ball.orbitRadiusAdditionPerFrame();
float orbitDegree = move.speedAdjust(frameCount, ball.orbitDegreesPerFrame());
float locationX = move.directionOfX() * sin(radians(orbitDegree)) * orbitRadius;
float locationY = move.directionOfY() * cos(radians(orbitDegree)) * orbitRadius;
float ballColor = ball.hueAtFrame(frameCount);
float ballSize = ball.sizeAtFrame(frameCount);
background(0.0, 0.0, 90.0, 100.0);
fill(ballColor, 80.0, 50.0, 100.0);
ellipse(locationX, locationY, ballSize, ballSize);
move.needsTurnover(locationX, locationY);
ナニコレ?
ボールの座標、色や大きさをここで計算して描画するという考え方のようですが、普通こうしない?
ball.move();
ball.display();
そして諸々の計算は ball オブジェクトが行うように書くのが一般的な気がします。(今の私には)
ということで draw() 中のコードを ball に移動してみたのがこちら。
/**
* お題:ネーミングの大切さリベンジ
* draw() を単純に
*
* @author @deconbatch
* @version 0.2
* Processing 3.2.1
* 2019.11.16
*/
/* ------------------------------ */
class Video {
int fps;
int seconds;
Video() {
fps = 30;
seconds = 10;
}
int totalFrames() {
return fps * seconds;
}
}
/* ------------------------------ */
class Ball {
int lap;
int totalFrames;
int directionX;
int directionY;
float easingMoveFactor;
int easingStartFrame;
float locationX;
float locationY;
float ballColor;
float ballSize;
Ball(int _frame) {
totalFrames = _frame;
lap = 3;
directionX = 1;
directionY = 1;
resetEasingMove();
}
float orbitDegreesPerFrame() {
return lap * 360.0 / totalFrames;
}
float orbitRadiusAdditionPerFrame() {
return max(width, height) * 1.0 / totalFrames;
}
float hueAtFrame(int _frame) {
return _frame * 360.0 / totalFrames;
}
float sizeAtFrame(int _frame) {
return map(_frame * 1.0 / totalFrames, 0.0, 1.0, 10.0, 100.0);
}
void resetEasingMove() {
easingMoveFactor = 0.0;
easingStartFrame = frameCount;
}
int directionOfX() {
return directionX;
}
int directionOfY() {
return directionY;
}
float speedAdjust(int frame, float degreesPerFrame) {
easingMoveFactor += 0.0061; // magic number
float fixedDegrees = easingStartFrame * degreesPerFrame;
float easedDegrees = (frameCount - easingStartFrame) * degreesPerFrame * easingCalculation(easingMoveFactor);
return fixedDegrees + easedDegrees;
}
float easingCalculation(float factor) {
// Hint from http://www.fal-works.jp/entry/2017/12/13/235330
return 2.0 * (1.0 - pow(min(1.0, factor) - 1.0, 4));
}
void needsTurnover(float x, float y) {
if (outOfRangeX(x)) {
directionX = turnover(directionX);
resetEasingMove();
}
if (outOfRangeY(y)) {
directionY = turnover(directionY);
resetEasingMove();
}
}
Boolean outOfRangeX(float x) {
if (x < 0 || x > width) {
return true;
}
return false;
}
Boolean outOfRangeY(float y) {
if (y < 0 || y > height) {
return true;
}
return false;
}
int turnover(int direction) {
return direction * -1;
}
void move(int _frame) {
float orbitRadius = _frame * orbitRadiusAdditionPerFrame();
float orbitDegree = speedAdjust(_frame, orbitDegreesPerFrame());
locationX = directionOfX() * sin(radians(orbitDegree)) * orbitRadius;
locationY = directionOfY() * cos(radians(orbitDegree)) * orbitRadius;
ballColor = hueAtFrame(_frame);
ballSize = sizeAtFrame(_frame);
needsTurnover(locationX, locationY);
}
void display() {
fill(ballColor, 80.0, 50.0, 100.0);
ellipse(locationX, locationY, ballSize, ballSize);
}
}
/* ------------------------------ */
Video video;
Ball ball;
void setup() {
size(640, 640);
colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
noStroke();
frameRate(30);
video = new Video();
ball = new Ball(video.totalFrames());
}
void draw() {
background(0.0, 0.0, 90.0, 100.0);
ball.move(frameCount);
ball.display();
if (frameCount > video.totalFrames()) {
exit();
}
}
間違いを正す!
これで draw() はシンプルになり、Move クラスは要らなくなりました。
でも、Move クラスの中身が Ball クラスに移動しただけなので実質何も変わってません。
あと、ボールの動きを計算する部分がさっぱりわかりません。
ロジックも間違ってるところがあるように見えます。
こことか本当ひどい… イージング勘違いしてない?
float speedAdjust(int frame, float degreesPerFrame) {
easingMoveFactor += 0.0061; // magic number
float fixedDegrees = easingStartFrame * degreesPerFrame;
float easedDegrees = (frameCount - easingStartFrame) * degreesPerFrame * easingCalculation(easingMoveFactor);
return fixedDegrees + easedDegrees;
}
その辺りを全部見直してみます。
/**
* お題:ネーミングの大切さリベンジ
* ボールの動きを計算するコードを見直し
*
* @author @deconbatch
* @version 0.3
* Processing 3.2.1
* 2019.11.16
*/
/**
* Movie : hold movie parameters
*/
class Movie {
int fps;
int seconds;
Movie() {
fps = 30; // frames per second
seconds = 10; // time length of movie
}
int totalFrames() {
return fps * seconds;
}
}
/**
* Ball : hold ball parameters, calculate coordinates and display
*/
class Ball {
int totalFrames; // movie total frame number
int bounceNum; // ball bounce number
int direction; // ball moving direction
float ballX; // ball coordinate
float ballY; // ball coordinate
float ballColor; // ball hue value
float ballSize; // ball size
Ball(int _totalFrames) {
totalFrames = _totalFrames;
bounceNum = 9;
direction = 1;
}
void move(int _frame) {
ballX = sin(orbitRadian(_frame)) * orbitRadius(_frame);
ballY = cos(orbitRadian(_frame)) * orbitRadius(_frame);
ballColor = hueAtFrame(_frame);
ballSize = sizeAtFrame(_frame);
checkBounce(_frame);
}
void display() {
noStroke();
fill(ballColor, 90.0, 40.0, 100.0);
ellipse(ballX, ballY, ballSize, ballSize);
}
float orbitRadius(int _frame) {
float orbitRadiusPerFrame = max(width, height) * 1.0 / totalFrames;
return _frame * orbitRadiusPerFrame;
}
float orbitRadian(int _frame) {
float bounceRate = ((_frame - 1) % framesPerBounce()) * 1.0 / framesPerBounce();
float easeRate = easeInPow(bounceRate);
if (direction < 0) {
return HALF_PI * (1.0 - easeRate);
}
return HALF_PI * easeRate;
}
int framesPerBounce() {
return ceil(totalFrames * 1.0 / bounceNum);
}
float easeInPow(float _t) {
return 1.0 - pow(_t - 1.0, 2);
}
float hueAtFrame(int _frame) {
return _frame * 240.0 / totalFrames + 120.0;
}
float sizeAtFrame(int _frame) {
return map(((_frame - 1) % framesPerBounce()) * 1.0 / framesPerBounce(), 0.0, 1.0, 15.0, 10.0) * map(_frame * 1.0 / totalFrames, 0.0, 1.0, 1.0, 4.0);
}
void checkBounce(int _frame) {
if (_frame % framesPerBounce() == 0) {
direction *= -1;
}
}
}
/**
* main.
*/
Movie movie;
Ball ball;
void setup() {
size(640, 640);
colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
frameRate(30);
movie = new Movie();
ball = new Ball(movie.totalFrames());
}
void draw() {
background(0.0, 0.0, 90.0, 100.0);
ball.move(frameCount);
ball.display();
if (frameCount >= movie.totalFrames()) {
exit();
}
}
うん、だいぶ読みやすくなったんじゃないでしょうか。
と言うか、ほとんど全面書き直しです。
ついでに動きや色にもちょっと手を加えてみました。
書き直して当時の疑問点はどうなったのか?
今回の全面書き直しで当時疑問に思った点がどうなったか見てみます。
名前が長すぎ。もっと簡潔にできないか?
ball.orbitRadiusAdditionPerFrame();
ここは当時のコードでは半径の増分を計算していたのでこういう名前になっていました。
今回は考え方を変えて、その時点の半径そのものを計算する関数にしたのでこういう名前にしました。
orbitRadius(_frame);
ここは解決ですね。
locationX かな? ballX じゃなくて?
どっちでもいい気がしますが、ボールの色とサイズが ballColor、ballSize なので、これに合わせて ballX の方がよいでしょう。
「切り返しの必要あります?」って疑問形の意味で書きたかったんだけど、伝わってる?
move.needsTurnover(locationX, locationY);
ここは跳ね返りをチェックという意味にして、
checkBounce(_frame);
としました。これも OK でしょう。
これは他にやりようなかったかな?
ball = new Ball(video.totalFrames());
ここは今回も同様になりました。
これいいのかな?というのは未だに疑問です。
Video クラス無くして、Ball クラスに全部持たせたほうがいい気もします。
ボールが自身のアニメーションの長さ、FPS を持っててもそれほど不自然じゃないのでは?
不自然かな?🤔
やっぱりわかんない。
スピード調整という意味がいまいち伝わらない感じ。
move.speedAdjust(frameCount, ball.orbitDegreesPerFrame());
ここは全面書き直しで跡形もなく消え去りました。
今回はボールの座標計算のための角度を下記の関数で計算しています。
float orbitRadian(int _frame) {
float bounceRate = ((_frame - 1) % framesPerBounce()) * 1.0 / framesPerBounce();
float easeRate = easeInPow(bounceRate);
if (direction < 0) {
return HALF_PI * (1.0 - easeRate);
}
return HALF_PI * easeRate;
}
イージングの計算がちょっと複雑な式になっちゃいましたが、何をしたいかは伝わるんじゃないかと。
だからここも OK!
これどっちがわかりやすいんだろう?
frameCount * ball.orbitRadiusAdditionPerFrame();
ball.sizeAtFrame(frameCount);
今なら言える。断然後者!
遊んでいても成長するもんだ
今回過去に書いたコードを読み直してみて、自分が書いたコードなのに「なんでこうしてるんだろう?」とか「こうすればいいのに!」と思うところが沢山出てきました。
日々遊んでるだけですが、一年でいくらかは成長してるみたいです。
向上したい点をもっと意識しながら遊べばもっともっと成長できるのかも!💪
2016年当時からの私のコーディング成長記録。どれぐらい成長してるかな?
この記事が面白かったらサポートしていただけませんか? ぜんざい好きな私に、ぜんざいをお腹いっぱい食べさせてほしい。あなたのことを想いながら食べるから、ぜんざいサポートお願いね 💕