2016 API for Game Development Clean Code Functions

  • Slides: 23
Download presentation
2016 API for Game Development Clean Code Functions Masaki Hayashi

2016 API for Game Development Clean Code Functions Masaki Hayashi

Functions. . . int ABCFunction( int input, string params ) {. . . return

Functions. . . int ABCFunction( int input, string params ) {. . . return value; } • Function is a very basic form of program. • It could be method (member). • It does something taking arguments and returns value. • What we concern is: Function names Argument names How many arguments Length of main body Function itself (its scope) etc. . .

The bad example. . . public void Closeup( object target, E_CAMERA_TARGET targettype , float

The bad example. . . public void Closeup( object target, E_CAMERA_TARGET targettype , float speed , E_PARSER_PARAMETER_TYPE speed. Type , bool dolly , bool track , E_CAMERA_TYPE type , E_CAMERA_TRANSITION transition , float adjx, float adjy, float adjz, float adjrx, float adjry, float adjrz, float adjvangle , float adjpan, float adjtilt, float adjzoom, bool skipscript) { float minx, miny, minz, maxx, maxy, maxz, d; minx=miny=minz=maxx=maxy=maxz=d=0. 0 f; float sx, sy, sz, tx, ty, tz, dist; float pp, tt; float dist. Closeup. Dolly; // dolly=trueのときのキャラとカメラの距離. float closeup. Adjust. Y = 0. 0 f; // 顔の大きさに対するカメラ注視位置のyずれの比率(人の場合ちょ・ニしたをねら・トバストショットにするため). float closeup. Adjust. Zoom = 0. 0 f; // 画面上で顔の大きさを少し小さくする。closeup. Adjust. Zoomは画面い・マいに対する比率. Vector 3 v 0, v 1, v 2, v 3, v 4, v 5; v 0 = Vector 3. zero; v 1 = Vector 3. zero; v 2 = Vector 3. zero; v 3 = Vector 3. zero; v 4 = Vector 3. zero; v 5 = Vector 3. zero; const float aspect. Ratio = 4. 0 f/3. 0 f; // 画面アスペクトが4:3(横:縦). cammove. closeup. target = target; cammove. closeup. targettype = targettype; cammove. closeup. speed = speed; cammove. closeup. speed. Type = speed. Type; cammove. closeup. dolly = dolly; cammove. closeup. track = track; cammove. closeup. type = type; cammove. closeup. adjx = adjx; cammove. closeup. adjy = adjy; cammove. closeup. adjz = adjz; cammove. closeup. adjrx = adjrx; cammove. closeup. adjry = adjry; cammove. closeup. adjrz = adjrz; cammove. closeup. adjvangle = adjvangle; cammove. closeup. adjpan = adjpan; cammove. closeup. adjtilt = adjtilt; cammove. closeup. adjzoom = adjzoom; initmove(E_PARSER_PARAMETER_IS. EPPI_SPECIFIED , E_PARSER_PARAMETER_IS. EPPI_SPECIFIED , speed. Type); if(targettype == E_CAMERA_TARGET. ECT_CHARACTER) { dist. Closeup. Dolly=2. 0 f; closeup. Adjust. Y = 0. 27 f; //0. 27 f closeup. Adjust. Zoom = 1. 65 f;

The bad example. . . TCharacter actor = (TCharacter)target; actor. get. Head. Alignment 2(ref

The bad example. . . TCharacter actor = (TCharacter)target; actor. get. Head. Alignment 2(ref v 0, ref v 1, ref v 2, ref v 3, ref v 4, ref v 5, ref d); } else if(targettype == E_CAMERA_TARGET. ECT_PROP) dist. Closeup. Dolly=2. 0 f; closeup. Adjust. Y = 0. 0 f; closeup. Adjust. Zoom = 1. 0 f; { TProp prop = (TProp)target; prop. get. Alignment(ref minx, ref miny, ref minz, ref maxx, ref maxy, ref maxz, ref d); v 0. x = minx; v 0. y = miny; v 0. z = minz; v 1. x = minx; v 1. y = maxy; v 1. z = maxz; v 2. x = maxx; v 2. y = miny; v 2. z = minz; v 3. x = minx; v 3. y = miny; v 3. z = maxz; v 4. x = maxx; v 4. y = maxy; v 4. z = minz; v 5. x = maxx; v 5. y = maxy; v 5. z = maxz; }else{ return; } // 頭のバウンディングボックスの中点を求める. tx = (v 0. x + v 5. x) * 0. 5 f; ty = (v 0. y + v 5. y) * 0. 5 f; tz = (v 0. z + v 5. z) * 0. 5 f; cammove. closeup. tx = tx; cammove. closeup. ty = ty; cammove. closeup. tz = tz; if(dolly == true) // キャラの前方dist. Closeup. Dollyにカメラを持・トくる(2 m). { sx = tx + dist. Closeup. Dolly*Mathf. Sin((3. 141592 f/180. 0 f)*d); sy = ty; sz = tz + dist. Closeup. Dolly*Mathf. Cos((3. 141592 f/180. 0 f)*d); } else // 現在のカメラ位置のまま. { sx = camdata. x; sy = camdata. y; sz = camdata. z; } // カメラのパンとチルトを計算して、頭中心にカメラを向ける. dist = Mathf. Sqrt((sx-tx)*(sx-tx) + (sy-ty)*(sy-ty) + (sz-tz)*(sz-tz)); pp = -(180. 0 f/3. 141592 f) * Mathf. Atan 2((tx-sx), (sz-tz)); tt = (180. 0 f/3. 141592 f) * Mathf. Asin((sy- ty)/dist); Matrix 4 x 4 mtx = Matrix 4 x 4. identity; Vector 3 vt 0, vt 1, vt 2, vt 3, vt 4, vt 5; vt 0 = new Vector 3(); vt 1 = new Vector 3(); vt 2 = new Vector 3(); vt 3 = new Vector 3(); vt 4 = new Vector 3(); vt 5 = new Vector 3();

The bad example. . . TMath. set. Rotation. Matrix. X(ref mtx, tt); // (3)rx回転する(チルト) TVMLのカメラの回転方向はワールドに対してだとrxとrzは逆になるので、符合にマイナスが付かない.

The bad example. . . TMath. set. Rotation. Matrix. X(ref mtx, tt); // (3)rx回転する(チルト) TVMLのカメラの回転方向はワールドに対してだとrxとrzは逆になるので、符合にマイナスが付かない. TMath. set. Rotation. Matrix. Y(ref mtx, -pp); // (2)ry回転して(パン). TMath. set. Translation. Matrix(ref mtx, -sy, -sz); // (1)ワールドを(sx, sy, sz)移動して. vt 0. x = v 0. x; vt 0. y = v 0. y; vt 0. z = v 0. z; vt 1. x = v 1. x; vt 1. y = v 1. y; vt 1. z = v 1. z; vt 2. x = v 2. x; vt 2. y = v 2. y; vt 2. z = v 2. z; vt 3. x = v 3. x; vt 3. y = v 3. y; vt 3. z = v 3. z; vt 4. x = v 4. x; vt 4. y = v 4. y; vt 4. z = v 4. z; vt 5. x = v 5. x; vt 5. y = v 5. y; vt 5. z = v 5. z; TMath. transform. Vect(mtx, ref vt 0); TMath. transform. Vect(mtx, ref vt 1); TMath. transform. Vect(mtx, ref vt 2); TMath. transform. Vect(mtx, ref vt 3); TMath. transform. Vect(mtx, ref vt 4); TMath. transform. Vect(mtx, ref vt 5); // ワールドにおける頂点のベクトルをセットする(6つ分). // カメラ位置&姿勢におけるローカル・Wかもヘた6つの頂点ベクトルの値(・W)を計算する. float[] xx = new float[8]; float[] yy = new float[8]; // 透視変換の計算。焦点距離 1. 0におけるxy平面の上の6つの頂点のxy・Wを求める. xx[0] = vt 0. x / vt 0. z; yy[0] = vt 0. y / vt 0. z; xx[1] = vt 1. x / vt 1. z; yy[1] = vt 1. y / vt 1. z; xx[2] = vt 2. x / vt 2. z; yy[2] = vt 2. y / vt 2. z; xx[3] = vt 3. x / vt 3. z; yy[3] = vt 3. y / vt 3. z; xx[4] = vt 4. x / vt 4. z; yy[4] = vt 4. y / vt 4. z; xx[5] = vt 5. x / vt 5. z; yy[5] = vt 5. y / vt 5. z; float xmax, xmin, ymax, ymin; xmax = ymax = -1 E 10 f; // 小さな値. xmin = ymin = 1 E 10 f; // でかい値. // xy平面における最大点と最小点を求める. for(int i=0 ; i<6 ; i++) { if(xx[i]>xmax) if(xx[i]<xmin) xmin=xx[i]; if(yy[i]>ymax) if(yy[i]<ymin) ymin=yy[i]; } xmax=xx[i]; ymax=yy[i]; // 画角を決めるため、中心から一番離れたxとyの値を求める. float dx, dy, dd; if (Mathf. Abs(xmin) > Mathf. Abs(xmax)) dx = Mathf. Abs(xmin); else dx = Mathf. Abs(xmax); if (Mathf. Abs(ymin) > Mathf. Abs(ymax)) dy = Mathf. Abs(ymin); else dy = Mathf. Abs(ymax); // 顔が横幅が大きいor縦長か、で、どちらの値から画角を計算するかを、決定する. if (dy*aspect. Ratio > dx) dd = dy; else dd = dx/aspect. Ratio; // 画面上で顔の大きさを少し小さくする。closeup. Adjust. Zoomは画面い・マいに対する比率。すなわちズームを少し引き気味にする. dd *= closeup. Adjust. Zoom; float ff; // 焦点距離 1. 0における原点とy方向の距離から垂直画角を計算する. ff = (180. 0 f / 3. 141592 f) * Mathf. Atan 2(1. 0 f, dd) * 2. 0 f; // ちょ・ニ下を狙・トバストショットにする。closeup. Adjust. Yは顔の大きさに対するカメラ中止位置のyずれの比率. float uuu = (180. 0 f/3. 141592 f) * Mathf. Atan 2(dd*closeup. Adjust. Y, 1. 0 f); cammove. closeup. uuu = uuu; // Track=onに使うためにここで保存しておく. 5

The bad example. . . cammove. closeup. rx = tt + uuu; cammove. closeup.

The bad example. . . cammove. closeup. rx = tt + uuu; cammove. closeup. ry = pp; cammove. closeup. rz = 0. 0 f; cammove. closeup. x = sx; cammove. closeup. y = sy; cammove. closeup. z = sz; cammove. closeup. vangle = ff; // Track=onのときのために保存. cammove. closeup. prerx = cammove. closeup. rx; cammove. closeup. prery = cammove. closeup. ry; cammove. closeup. prerz = cammove. closeup. rz; cammove. closeup. x += adjx; cammove. closeup. y += adjy; cammove. closeup. z += adjz; cammove. closeup. rx += adjrx; cammove. closeup. ry += adjry; cammove. closeup. rz += adjrz; cammove. closeup. vangle += adjvangle; destf = cammove. closeup. vangle = 114. 59156 f * Mathf. Atan 2( 1. 0 f, (Mathf. Tan(0. 008726646 f * cammove. closeup. vangle) * adjzoom) ); // 114. 59156 f = 180. 0 / PI * 2. 0 です。後の数字はその逆. cammove. closeup. rx += (cammove. closeup. vangle * adjtilt); destfh = 114. 59156 f * Mathf. Atan 2(aspect. Ratio * Mathf. Tan(0. 008726646 f * cammove. closeup. vangle), 1. 0 f); // 垂直画角を水平画角に変換. cammove. closeup. ry -= (destfh * adjpan); cammove. closeup. ry += 180. 0 f; //calccameraに記述されていた内容をこちらに移植 if(skipscript == false) { cammove. closeup. trigger = true; cammove. closeup. status = true; cammove. type = type; cammove. speed = speed; cammove. closeup. transition = transition; } else { camdata. x = cammove. closeup. x; camdata. y = cammove. closeup. y; camdata. z = cammove. closeup. z; camdata. rx = cammove. closeup. rx; camdata. ry = cammove. closeup. ry; camdata. rz = cammove. closeup. rz; camdata. vangle = cammove. closeup. vangle; cammove. closeup. status = false; } } About 240 lines. . . (´Д`). 6

Make it small! They should be smaller than small. . . How short should

Make it small! They should be smaller than small. . . How short should it be? It could be more than 1000 lines! In the past, it corresponds to the screen size (24 lines) It could be 2 to 5 lines. Number of function would increase, though.

Make it small! void ABCFunction( int input ) // do A // do B

Make it small! void ABCFunction( int input ) // do A // do B // do C } { void ABCFunction( int input ) calculate. A( input ); calcultae. B ( input ); calculate. C ( input ); } void calculate. A( int input ) // do A } void calculate. B( int input ) // do B } void calculate. C( int input ) // do C } { Break down into smaller pieces { { {

Blocks and indenting It should not large enough to hold nested structures. The indent

Blocks and indenting It should not large enough to hold nested structures. The indent level should be less than 1 or 2. if(camera. is. Perspective) // do something if(status) { // do something if(flag == OMITTED) // do something } } } 1 { { 2 3 should be avoided. no more than 3. . .

Do one thing There's an old saying. . . Functions should do one thing

Do one thing There's an old saying. . . Functions should do one thing They should do it well They should do it only. Don't make a function do different type of jobs. If you cannot extract a new function from the function that's the final.

A function doing multiple jobs void Write. Out. Html. Contents. To. Text. From. Given.

A function doing multiple jobs void Write. Out. Html. Contents. To. Text. From. Given. Url ( string url ) { WWW www = new WWW (url); while (!www. is. Done) ; string html = www. text; Match m 1 = Regex. Match(html, "<title>(. *? )</title>"); Match m 2 = Regex. Match(html, "<img\s*src\s*=\s*"(. *? )"\s*. *? >"); string title = m 1. Groups[1]. Value; string image. Url = m 2. Groups[1]. Value; string buff = "title: " + title + "n"; buff += "image: " + image. Url + "n"; } string file. Name = "output. txt"; System. IO. File. Write. All. Text(file. Name, buff);

One level of abstraction per function Don't mix different abstraction level of lower and

One level of abstraction per function Don't mix different abstraction level of lower and higher. If so, separate it to let one function take one abstraction level. void add. Character(string model. Name) { Character actor = get. Character(model. Name); string buff = "actor = " + actor. name + "n"; append. DB(buff); } void add. Character(string model. Name) { Character actor = get. Character(model. Name); add. Actor. To. DB(actor); } void add. Actor. To. DB(Character actor) { string buff = "actor = " + actor. name + "n"; append. DB(buff); }

Reading code from top to bottom Let the code be read like a top-down

Reading code from top to bottom Let the code be read like a top-down narrative. Code public aaa (xxxx) {. . } public bbb(xxxx) {. . } public ccc (xxxx) {. . } public ddd (xxxx) {. . } public eee (xxxx) {. . } public fff (xxxx) {. . } public ggg (xxxx) {. . } public hhh (xxxx) {. . }. . Abstraction level High Low TO - paragraphs To do aaa, we do bbb, then we do ccc. To do bbb, we do ddd. To do ccc, we do eee. To do ddd, . . . .

Switch statements "Switch" statements always do N things. So, it may violate some basic

Switch statements "Switch" statements always do N things. So, it may violate some basic rules regarding function: e. g. - It may induce multiple switch at multiple functions. - It may break Single Responsibility Principle One solution: Bury the switch statement in a low-level class and use polymorphism.

Single Responsibility Principle (SRP) A class should have one responsibility. A class should have

Single Responsibility Principle (SRP) A class should have one responsibility. A class should have only one reason to change. A good abstraction has a good name.

Use descriptive names Take time to choose a good name for a function. Good

Use descriptive names Take time to choose a good name for a function. Good name is for a good function that does one thing. e. g. chk() < check. Character. Type() Don't hesitate to make a long name. Make a descriptive name to work mostly like a comment. Don't hesitate to change it in the middle of the way. Be consistent

Function arguments Number of argument should be small. 0 (niladic) > 1 (monadic) >

Function arguments Number of argument should be small. 0 (niladic) > 1 (monadic) > 2 (dyadic) > 3 (triadic) should be avoided - Much arguments may reduce readability of the function. - It increases the difficulty when testing the function with much arguments.

Output arguments (out, ref) may be troublesome. int ABCFunction ( float a, float b,

Output arguments (out, ref) may be troublesome. int ABCFunction ( float a, float b, out float ans 1, out float ans 2 ); - It It If If may reduce readability. may make a test troublesome. it's only one, use return value. you need two or more, try think to use member variables.

Avoid flag arguments Passing a boolean is a terrible practice. It proclaims that this

Avoid flag arguments Passing a boolean is a terrible practice. It proclaims that this function does more than one thing. e. g. void render(bool is 3 D) You should split the function. e. g. void render 3 D() void render 2 D() void render(int flag) <--- worse. . .

Have no side effects Your function promises to do one thing, but it also

Have no side effects Your function promises to do one thing, but it also does other hidden things. It should be avoided. e. g. public bool is. Camera(string name) { Camera camera = get. Camera. By. Name(name); if(camera != null) { Camera. initialize(); return true; } return false; } It may cause a temporal coupling (terrible).

Don't repeat yourself Be aware of duplication in a code. public void append. Camera.

Don't repeat yourself Be aware of duplication in a code. public void append. Camera. To. LOG(string name) { Camera camera = get. Camera. By. Name(name); if(camera != null) { if(camera. type == "fps") { buffer = "#CAMERA INFO#n" buffer += "camera name: " + camera. name + "n"; buffer += " type: FPSn"; } else if(camera. type == "fixed"){ buffer = "#CAMERA INFO#n" buffer += "camera name: " + camera. name + "n"; buffer += " type: FIXEDn"; } else { buffer = "#CAMERA INFO#n" buffer += "camera name: " + camera. name + "n"; buffer += " type: UNKNOWNn"; } writedown(buffer); }

Don't repeat yourself Be aware of duplication in a code. public void append. Camera.

Don't repeat yourself Be aware of duplication in a code. public void append. Camera. To. LOG(string name) { Camera camera = get. Camera. By. Name(name); if(camera != null) { buffer = "#CAMERA INFO#n" buffer += "camera name: " + camera. name + "n"; buffer += " type: "; if(camera. type == "fps") buffer += " FPSn"; else if(camera. type == "fixed") buffer += " FIXEDn"; else buffer += " UNKNOWNn"; } writedown(buffer);

Final words Making software is like writing a paper or an article. System is

Final words Making software is like writing a paper or an article. System is a story to be told, rather than a program to be written. Make a draft first Massage it, restructure it, refine it It ended up with functions that follows the rules. No need to be perfect code from the first time.