20190127のUnityに関する記事は8件です。

[Unity]Android Studio 3.1.2 によるAndroidネイティブプラグイン作成

Androidネイティブプラグインの作り方

ネットを検索すればプラグインの作り方について書かれたサイトは沢山見つかりますが、どれも古い情報だったためか、すんなりと作成できなかったので、簡単にまとめてみます。

UnityPlayerActivityを継承したクラスを使ったプラグイン

ここにあるように、プラグインの作り方にはいくつか方法があるのですが、今回はUnityPlayerActivityを継承したクラスを使う方法について書いていきます。

  1. Android Studio のプロジェクトを新規作成して、Android Libraryモジュールを追加
  2. Unityのclasses.jarをlibsにコピー(classes.jarの場所は公式ドキュメントを参照)
  3. UnityPlayerActivityを継承したjavaクラスを追加
  4. プラグインにclasses.jarを含めないようにするため、gradleに
android.libraryVariants.all { variant ->
    variant.outputs.each { output ->
        output.packageLibrary.exclude('libs/classes.jar')
    }
}

を追加

Unityでビルドしようとするとclasses.jarのモジュールが競合してしまう

検索すると、大体上記の方法にたどり着くのですが、手順4でclasses.jarを除外することができません。(おそらく、gradleのバージョンが上がって、dependenciesにimplementation fileTree(dir: 'libs', include: ['*.jar'])と記述されているからだと思われます。以前はcompile~とかだったみたい。正しい情報を知りたい方はGradleの公式ドキュメントを見てください。)

色々調べた結果、implementation用のディレクトリの他にコンパイル時だけ参照するライブラリ用のディレクトリを追加し、そこにclasses.jarを置きます。そして、depencenciesにcompileOnlyでそのディレクトリを指定してやることで、classes.jarを含まないaarを出力することができました。

  1. Android Studio のプロジェクトを新規作成して、Android Libraryモジュールを追加
  2. compile_onlyディレクトリを新規作成し、Unityのclasses.jarをそこに配置
  3. UnityPlayerActivityを継承したjavaクラスを追加
  4. gradleのdependenciesにcompileOnly fileTree(dir: 'compile_only', include: ['*.jar'])を追加

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    compileOnly fileTree(dir: 'compile_only', include: ['*.jar'])

    implementation 'com.android.support:appcompat-v7:27.1.1'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

まとめ

classes.jarはcompileOnlyで追加する!
他の細かい手順は省略・・・(検索すれば大体わかるはず)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】TextMeshProで日本語を使えるようにする

はじめに

こちらが日本語を含めた文字コードをCustomRange形式で表記したものです。

32-128,8192-8303,12288-12543,65280-65519,19968,19971,19977,19978,19979,20013,20061,20108,20116,20154,20241,20808,20837,20843,20845,20870,20986,21147,21313,21315,21475,21491,21517,22235,22303,22805,22823,22825,22899,23376,23383,23398,23567,23665,24029,24038,24180,25163,25991,26085,26089,26376,26408,26412,26449,26519,26657,26862,27491,27671,27700,28779,29356,29577,29579,29983,30000,30007,30010,30333,30334,30446,30707,31354,31435,31481,31992,32819,33457,33609,34411,35211,35997,36196,36275,36554,37329,38632,38738,38899,19975,20024,20132,20140,20170,20250,20307,20309,20316,20803,20804,20809,20844,20869,20908,20992,20998,20999,21069,21271,21320,21322,21335,21407,21451,21476,21488,21512,21516,22238,22259,22269,22290,22320,22580,22768,22770,22799,22806,22810,22812,22826,22969,22985,23460,23478,23546,23569,23721,24037,24066,24112,24195,24215,24339,24341,24351,24369,24375,24403,24418,24460,24515,24605,25144,25165,25945,25968,26032,26041,26126,26143,26149,26172,26178,26228,26332,26360,26397,26469,26481,27005,27468,27490,27497,27597,27598,27611,27744,27773,27963,28023,28857,29238,29275,29702,29992,30011,30058,30452,30690,30693,31038,31179,31185,31572,31639,31859,32025,32048,32068,32117,32218,32701,32771,32862,32905,33258,33337,33394,33590,34892,35199,35242,35282,35328,35336,35352,35441,35486,35501,35895,36023,36208,36817,36890,36913,36947,36960,37324,37326,38263,38272,38291,38634,38642,38651,38957,38996,39080,39135,39318,39340,39640,39770,40165,40180,40614,40644,40658,19969,19990,20001,20027,20055,20104,20107,20181,20182,20195,20303,20351,20418,20493,20840,20855,20889,21015,21161,21193,21205,21213,21270,21306,21307,21435,21453,21462,21463,21495,21521,21531,21619,21629,21644,21697,21729,21830,21839,22338,22830,22987,22996,23432,23433,23450,23455,23458,23470,23487,23506,23550,23616,23627,23736,23798,24030,24115,24179,24184,24230,24235,24237,24335,24441,24453,24613,24687,24746,24754,24819,24847,24863,25152,25171,25237,25342,25345,25351,25918,25972,26053,26063,26132,26157,26257,26263,26354,26377,26381,26399,26495,26609,26681,26893,26989,27096,27178,27211,27425,27503,27515,27703,27770,27833,27874,27880,27891,27915,27969,28040,28145,28201,28207,28246,28271,28450,28845,29289,29699,30001,30003,30028,30033,30149,30330,30331,30382,30399,30456,30476,30495,30528,30701,30740,31036,31070,31085,31119,31186,31350,31456,31461,31515,31532,31558,31561,31665,32026,32066,32209,32244,32650,32654,32722,32773,32946,33510,33655,33853,33865,34220,34880,34920,35433,35519,35527,35910,36000,36215,36335,36523,36578,36605,36786,36820,36861,36865,36895,36914,36938,36939,37096,37117,37197,37202,37325,37444,37504,38283,38498,38525,38542,38598,38754,38988,39154,39208,39365,40763,19981,20105,20184,20196,20197,20210,20253,20301,20302,20363,20415,20449,20489,20505,20511,20572,20581,20596,20685,20740,20806,20816,20849,20853,20856,20919,21021,21029,21033,21047,21103,21151,21152,21162,21172,21191,21253,21330,21332,21336,21338,21360,21442,21490,21496,21508,21578,21608,21809,21916,22120,22258,22266,22411,22530,22633,22763,22793,22827,22833,22909,23395,23403,23436,23448,23475,23519,24035,24046,24076,24109,24111,24213,24220,24247,24314,24452,24466,24471,24517,24565,24859,25104,25126,25240,25369,25913,25937,25943,25955,26009,26071,26152,26223,26368,26395,26410,26411,26413,26448,26463,26494,26524,26628,26696,26757,26800,26997,27161,27231,27424,27508,27531,27578,27602,27663,27665,27714,27835,27861,27875,27973,28020,28165,28288,28417,28783,28961,28982,28988,29031,29105,29287,29305,29987,30340,30465,31069,31080,31278,31309,31478,31505,31649,31680,31881,32000,32004,32080,32102,32154,32622,32769,32963,33032,33144,33251,33322,33391,33464,33469,33521,33756,34903,34915,35201,35226,35251,35347,35430,35500,35506,35696,35937,36008,36015,36027,36062,36557,36650,36766,36794,36899,36948,36984,37089,37327,37682,37857,38306,38520,38538,38745,38918,39000,39006,39131,39151,39178,39443,20037,20175,20206,20214,20219,20284,20313,20385,20445,20462,20469,20491,20633,20687,20877,21002,21028,21046,21048,21063,21177,21209,21218,21402,21477,21487,21942,22240,22243,22311,22312,22343,22522,22577,22659,22675,22679,22818,22971,23142,23481,23492,23500,23566,23621,23646,24067,24107,24120,24185,24207,24321,24373,24448,24489,24499,24535,24540,24555,24615,24681,24773,24907,24931,25215,25216,25307,25480,25505,25509,25552,25613,25903,25919,25925,25973,26029,26087,26131,26292,26465,26525,26619,26684,26716,26908,27083,27494,27604,27704,27827,28082,28151,28187,28204,28310,28436,28500,28797,29123,29256,29359,29366,29420,29575,29694,30041,30053,30410,30524,30772,30906,31034,31062,31105,31227,31243,31246,31689,31934,32032,32076,32113,32118,32191,32207,32232,32318,32340,32618,32676,32681,32789,32887,32933,33021,33288,33292,33294,34899,34907,35069,35079,35215,35299,35373,35377,35388,35413,35611,35613,35672,35703,35914,36001,36007,36012,36024,36031,36032,36039,36059,36074,36664,36848,36855,36864,36870,36896,36942,36969,37240,37489,37509,37549,38450,38480,38522,38555,38609,38750,38928,38936,38989,39164,20006,20081,20083,20129,20161,20379,20467,20516,20663,20778,20826,20874,20966,21051,21106,21109,21127,21220,21361,21365,21427,21454,21518,21542,21560,21628,21892,22256,22402,22478,22495,22863,22894,23039,23384,23389,23429,23431,23447,23449,23453,23459,23494,23544,23554,23556,23558,23562,23601,23610,23626,23637,23652,24049,24059,24149,24178,24188,24193,24231,24310,24459,24467,24536,24544,25010,25105,25209,25285,25309,25313,25448,25506,25512,25582,25805,25964,26144,26217,26262,26286,26391,26426,26522,26579,26666,26834,27169,27177,27193,27442,27573,27839,27849,27927,27966,28168,28304,28526,28608,28784,29087,29255,29677,30064,30097,30171,30343,30427,30431,30475,30722,30913,31169,31192,31296,31348,31379,31563,31574,31777,31958,31995,32005,32013,32020,32121,32294,32302,32626,32716,32854,32954,32972,33016,33075,33145,33235,33256,33267,33509,33879,33976,34101,34453,34886,35009,35013,35023,35036,35222,35239,35342,35370,35379,35422,35468,35469,35477,35488,35492,35542,35576,35686,36020,36035,36986,37109,37111,37341,37628,38281,38307,38477,38491,38500,38556,38627,38761,38914,39592,19976,19982,19988,19992,19993,20025,20047,20057,20094,20102,20114,20117,20124,20139,20141,20171,20185,20208,20225,20239,20240,20271,20276,20280,20282,20294,20304,20339,20341,20365,20381,20398,20399,20405,20419,20426,20439,20472,20498,20515,20523,20537,20553,20559,20597,20598,20605,20621,20625,20632,20652,20661,20670,20693,20698,20711,20736,20754,20767,20805,20811,20813,20860,20882,20887,20896,20934,20941,20957,20961,20982,20984,20985,20995,21000,21009,21040,21050,21066,21078,21083,21091,21092,21104,21155,21169,21182,21189,21208,21215,21223,21234,21242,21249,21280,21305,21311,21319,21329,21331,21344,21363,21364,21368,21380,21400,21448,21450,21452,21460,21465,21483,21484,21513,21519,21520,21535,21547,21561,21576,21577,21682,21696,21746,21766,21767,21776,21807,21843,21914,21917,21930,21931,21987,22022,22065,22132,22151,22234,22287,22346,22353,22378,22435,22475,22519,22521,22528,22533,22549,22564,22570,22592,22593,22602,22609,22612,22615,22618,22654,22684,22696,22707,22718,22721,22727,22730,22732,22766,22769,22855,22857,22865,22868,22885,22888,22890,22900,22914,22915,22916,22922,22937,22949,22952,22995,23019,23035,23041,23064,23072,23087,23110,23130,23167,23186,23233,23244,23265,23330,23380,23396,23452,23472,23476,23477,23490,23515,23517,23521,23527,23529,23534,23551,23553,23561,23563,23578,23612,23613,23614,23615,23624,23653,23663,23696,23724,23731,23776,23777,23792,23815,23822,23849,24033,24039,24040,24070,24093,24101,24125,24133,24163,24187,24189,24190,24202,24246,24248,24259,24265,24266,24311,24330,24336,24340,24358,24359,24382,24425,24427,24432,24433,24444,24449,24464,24481,24490,24494,24500,24505,24524,24525,24537,24594,24598,24608,24618,24651,24656,24658,24677,24680,24685,24693,24724,24735,24736,24739,24742,24745,24764,24785,24796,24808,24816,24833,24841,24858,24904,24908,24910,24917,24930,24936,24942,24944,24950,24962,24974,24996,25001,25014,25022,25031,25040,25074,25080,25106,25135,25147,25151,25159,25161,25173,25201,25206,25220,25226,25233,25239,25244,25246,25259,25265,25269,25273,25276,25277,25293,25296,25298,25299,25304,25305,25312,25324,25335,25361,25375,25391,25407,25429,25436,25454,25475,25484,25490,25496,25499,25511,25514,25522,25551,25562,25563,25569,25588,25594,25644,25645,25658,25662,25666,25688,25705,25731,25764,25774,25778,25793,25830,25836,25915,25935,25954,25975,25993,25998,26007,26012,26020,26021,26045,26059,26082,26088,26092,26118,26119,26159,26222,26230,26241,26247,26278,26283,26311,26356,26361,26367,26389,26417,26420,26429,26441,26479,26512,26528,26530,26543,26550,26564,26576,26580,26611,26643,26680,26685,26691,26705,26719,26820,26827,26842,26847,26874,27004,27010,27133,27396,27431,27450,27454,27475,27507,27529,27530,27542,27572,27579,27583,27713,27735,27738,27743,27784,27798,27809,27810,27832,27836,27841,27850,27852,27873,27877,27888,27934,27941,27946,27972,27996,28006,28010,28014,28024,28057,28079,28092,28113,28129,28155,28167,28169,28171,28179,28193,28198,28286,28287,28317,28342,28357,28363,28369,28381,28382,28404,28418,28422,28431,28448,28459,28460,28472,28508,28511,28516,28548,28609,28611,28651,28655,28716,28809,28810,28814,28858,28872,28966,29017,29033,29038,29128,29157,29190,29237,29298,29344,29378,29417,29421,29467,29471,29483,29486,29494,29503,29508,29539,29554,29572,29645,29664,29748,29872,29885,29942,29976,29978,30002,30036,30044,30045,30067,30094,30123,30130,30142,30151,30168,30178,30196,30274,30290,30294,30342,30406,30423,30435,30436,30450,30462,30496,30522,30561,30563,30636,30683,30703,30741,30770,30813,30827,30828,30849,30865,30952,30977,30990,31048,31049,31077,31109,31117,31168,31199,31209,31216,31258,31282,31292,31295,31298,31311,31339,31361,31363,31378,31406,31407,31452,31471,31526,31570,31623,31684,31716,31807,31821,31883,31890,31895,31896,31899,31911,31975,31998,32011,32027,32033,32034,32043,32047,32051,32057,32058,32094,32097,32153,32173,32177,32178,32202,32210,32224,32233,32239,32257,32260,32283,32299,32321,32330,32341,32365,32368,32566,32624,32631,32645,32705,32763,32764,32784,32791,32884,32908,32918,32925,32930,32937,32938,32943,32966,32974,32990,33012,33026,33029,33050,33073,33081,33104,33109,33136,33178,33180,33192,33261,33268,33303,33310,33311,33324,33334,33351,33382,33419,33437,33459,33495,33538,33550,33618,33624,33738,33740,33747,33775,33900,33988,34180,34214,34218,34219,34281,34299,34384,34394,34396,34398,34442,34503,34509,34542,34701,34909,34913,34928,34935,34955,34987,35010,35029,35064,35088,35090,35167,35186,35206,35207,35302,35330,35351,35359,35380,35386,35408,35412,35424,35440,35442,35443,35463,35465,35475,35480,35531,35565,35566,35582,35584,35585,35588,35609,35617,35641,35676,35698,35930,35946,35998,36002,36009,36011,36036,36042,36051,36060,36064,36066,36070,36092,36104,36198,36212,36229,36234,36259,36317,36321,36339,36341,36362,36367,36493,36556,36562,36575,36600,36611,36617,36637,36649,36676,36763,36785,36796,36805,36814,36843,36845,36867,36879,36880,36883,36884,36893,36910,36920,36930,36933,36935,36941,36949,36963,36973,36974,36981,36983,36991,36996,37030,37034,37048,37066,37070,37101,37196,37204,37218,37226,37228,37237,37239,37276,37304,37320,37347,37389,37428,37467,37474,37507,37521,37528,37613,37619,37656,37664,37676,37679,37723,37782,37806,37912,37969,38289,38309,38322,38360,38459,38468,38499,38501,38506,38512,38515,38517,38518,38533,38534,38543,38548,38560,38563,38583,38587,38596,38597,38599,38604,38626,38640,38646,38647,38656,38663,38666,38684,38695,38706,38772,38907,38911,38917,38929,38930,38971,38972,38997,39015,39138,39165,39166,39187,39321,39364,39366,39376,39438,39442,39472,39514,39620,39658,39740,39746,39749,39764,39854,39912,40335,40599,40635,40665,40723,40802,20018,20028,20062,20096,20238,20406,20474,20658,20677,20901,20918,20932,21049,21085,21187,21246,21250,21489,21570,21610,21693,21754,21764,21822,21897,21929,21957,22066,22524,22534,22622,22625,22856,22934,22956,23195,23241,23451,23611,23713,23830,23888,24062,24324,24357,24409,24616,24675,24807,24900,24999,25004,25114,25140,25289,25325,25331,25334,25384,25387,25417,25431,25467,25711,26001,26028,26086,26106,26151,26326,26365,26517,26613,26623,26627,26689,26775,26792,26885,26894,26999,27584,27710,27726,27760,27779,27801,28139,28263,28346,28528,29006,29066,29226,29245,29273,29401,29609,29792,29827,29863,29926,30031,30079,30165,30185,30221,30473,30566,30637,30643,31293,31391,31627,31672,31840,32187,32251,32629,32670,32680,32920,32929,33031,33034,33102,33131,33146,33181,33203,33222,33276,33335,33398,33455,33499,33576,33806,33883,33995,34065,34109,34253,34276,34382,34425,34562,34588,34966,35070,35331,35427,35438,35504,35558,35559,35598,35980,36010,36028,36034,36077,36394,36468,36771,36956,36961,37027,37198,37266,37319,37340,37670,37678,37707,37749,37772,38343,38428,38442,38553,38867,38915,38920,38931,38960,38990,39173,39180,39378,39608,39729,40372,40575,40595,40634

導入

UnityのText Mesh Proアセットで日本語を使うときの手順

とてもわかりやすく解説されているので、こちらを参照すると良いと思います。

経緯

Qiitaで散見される文字コードのCustomRangeだと欠けているものがいくつかあるので、修正したものを公開しました。

https://gist.github.com/thorikawa/2856a7cf912349c0b6b7
コメントでも指摘されています。

全角文字が含まれていない

65280–6551965280-65519に変更
(ハイフンがダッシュになっている)

「…」などのリーダーが含まれていない

8192-8303又は8230を追加
(TMPのoverflowをEllipsisに設定したときに「…」が表示されない)

重複して定義している

12288-12543,12448-1254312288-12543に変更

表示されない文字がある場合

フォントに文字が含まれていないか、
指定した文字範囲に含まれていない可能性があります。

文字コードを探す場合には以下のようなサイトを使うと便利です。
- Unicode(ユニコード)一覧検索 & 逆引き検索「うにこ~ど2」
- Unicode Chart

参考資料

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SceneManagerで,ゲームのシーンを切り替えよう.(Ocu5Game.make)

SceneManagerとは

Unityがシーンをコントロールするclassを用意してくれています.

それが UnityEngine.SceneManagement classです.

   -SceneManager.Load    シーンを追加

   -SceneManager.Unload   シーンを削除

などを用いてシーンを切り替えます.

シーンを整理

A:バトルフィールド (BattleField) ・・・常時表示
B:タイトルシーン (Title)
C:ゲームプレイシーン (Play)
D:リザルトシーン (Result)

今回はシンプルに4つのシーンを組み合わせで,ゲームは構築されているとします.
Aのバトルプレイスは常時表示されており,それに載せるシーンが B → C → D 入れ替わっていくといった感じです.

具体例

現在,BattleFieldと,Titleシーンがあるところに,Playのシーンを追加する例を紹介します.

using UnityEngine;
using UnityEngine.SceneManagement; //デフォルトでは記載されていないので追加する.
    void ChangeScene()
    {   
        //Playシーンの追加
        SceneManager.LoadScene("Play", LoadSceneMode.Additive);
        //Titleシーンの削除
        SceneManager.UnloadSceneAsync("Title"); 
    }

LoadSceneMode.Additive は現在,A,Bのシーンがあるところに,Playのシーンを追加するといった状況の際につけます. 多くの場合で,このような状況だとは思われるので,LoadSceneMode.Additiveまでセットで覚えましょう.(反対に何もシーンがないところに,.Additiveでシーンを加えようとするとエラーになります.)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Jenkinsの「成果物を保存」で.appの保存に失敗した時の備忘録

状況

UnityでMac用アプリを書き出す時の話。

「保存するファイル」に以下を指定

target/*.app

コンソール出力

成果物を保存中
ERROR: Step ‘成果物を保存’ failed: 指定されたファイルパターン「target/YourProject.app」に合致するファイルがありません。設定ミス?
Finished: FAILURE

原因

.appの実態はディレクトリなので、ディレクトリとしてパスを指定する必要があった。

解決策

「保存するファイル」に以下を指定

target/*.app/

コンソール出力

成果物を保存中
Finished: SUCCESS
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】シェーダーで面を凸凹(デコボコ)する方法【Tessellation・Displacement】

shaderの勉強をしていると、こんな疑問が出てきませんか?

テクスチャの明度に応じて、面をデコボコさせたいけど、どうすればよいんだ?

ツール(Shader Graph, Shader Forge)を使って、面を凸凹するシェーダーを作成するやり方はwebに色々あるんですが、ツールなしの方法が見当たない…

途方に暮れていたのですが、奇跡的に知り合ったUnityコミュニティに質問したところ、やり方が判明したので、ご紹介します。サンプルソースもありますよ!

ちなみに、タイトルは私のように”シェーダー 平面 凸凹(でこぼこ)”とググっても出てくるように設定しました笑

※追記 2019/1/28
ここでは、頂点を上下させて面を凸凹させる方法をご紹介します。他にも、凹凸が「あるように」見せるバンプマッピングという手法がございます。こちらの方法は、以下の記事をご覧ください。
[Unity] バンプマッピング(法線マッピング)をやってみる

結論:できる!!!

結論から述べると、ツール無しでもシェーダーで平面を凸凹することはできます!

じゃぁ、なぜ”シェーダー 平面 凸凹(でこぼこ)”とググっても出てこないか?

それは平面を凸凹する方法は専門用語で存在しており、みんな専門用語で紹介しているからなんですね。なので、専門用語を知らないと、答えには辿り着かないという(^p^)

キーワードはTessellation・Displacement!

平面凸凹のキーワードは2つです!

  • Tessellation(テッセレーション):ポリゴンメッシュを細分割
  • Displacement(ディスプレイス):テクスチャの高さマップを描画時に評価して頂点を上下

テッセレーションでメッシュを再分割し、凹凸しやすくしてから、ディスプレイス(頂点を上下)します。イメージはこんな感じです!

サンプルソース

やらたいことがわかっても、肝心なソースがなければ、実装できないですよね。ご安心ください。

なんと、公式が提供している"Standard Assets"にあるんです(マジで神)

ソースの場所と中身がこちら!
スクリーンショット 2019-01-27 10.08.28.png
スクリーンショット 2019-01-27 10.08.35.png
スクリーンショット_2019-01-27_10_12_37.png

インポート後、Assets > Standard Assets > Effects > TessellationShadersにシェーダーやマテリアファイルが入っています!

ソースの中身はこんな感じ!

pedSpecularDisplacement.shader
Shader "Tessellation/Bumped Specular (displacement)" {
Properties {
    _Color ("Main Color", Color) = (1,1,1,1)
    _SpecColor ("Specular Color", Color) = (0.5, 0.5, 0.5, 1)
    _Shininess ("Shininess", Range (0.03, 1)) = 0.078125
    _Parallax ("Height", Range (0.0, 1.0)) = 0.5
    _MainTex ("Base (RGB) Gloss (A)", 2D) = "white" {}
    _BumpMap ("Normalmap", 2D) = "bump" {}
    _ParallaxMap ("Heightmap (A)", 2D) = "black" {}
    _EdgeLength ("Edge length", Range(3,50)) = 10
}
SubShader { 
    Tags { "RenderType"="Opaque" }
    LOD 800

CGPROGRAM
#pragma surface surf BlinnPhong addshadow vertex:disp tessellate:tessEdge
#include "Tessellation.cginc"

struct appdata {
    float4 vertex : POSITION;
    float4 tangent : TANGENT;
    float3 normal : NORMAL;
    float2 texcoord : TEXCOORD0;
    float2 texcoord1 : TEXCOORD1;
    float2 texcoord2 : TEXCOORD2;
};

float _EdgeLength;
float _Parallax;

float4 tessEdge (appdata v0, appdata v1, appdata v2)
{
    return UnityEdgeLengthBasedTessCull (v0.vertex, v1.vertex, v2.vertex, _EdgeLength, _Parallax * 1.5f);
}

sampler2D _ParallaxMap;

void disp (inout appdata v)
{
    float d = tex2Dlod(_ParallaxMap, float4(v.texcoord.xy,0,0)).a * _Parallax;
    v.vertex.xyz += v.normal * d;
}

sampler2D _MainTex;
sampler2D _BumpMap;
fixed4 _Color;
half _Shininess;

struct Input {
    float2 uv_MainTex;
    float2 uv_BumpMap;
};

void surf (Input IN, inout SurfaceOutput o) {
    fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
    o.Albedo = tex.rgb * _Color.rgb;
    o.Gloss = tex.a;
    o.Alpha = tex.a * _Color.a;
    o.Specular = _Shininess;
    o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap));
}
ENDCG
}

FallBack "Bumped Specular"
}

マテリアルをアタッチすると、凸凹した面ができあがります!
(マテリアルもインポートしたスタンダードアセット内にあります)
unity_qiita.gif

最後に

Tessellation・Displacementを使えば、シェーダーで面を凸凹できます。凸凹はテクスチャによるので、種類は無限大!

平面以外にも、棒などの様々なオブジェクトを凸凹したい時にも使えるので便利です。

公式のシェーダーを使えば簡単に実装できるので、ぜひ気になる方はお試しください!

参考記事

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

大学生が有料スマホゲームを作った全てを公開するよ(3)ゲームの構造・パズルとアクションの仕組み(前編)

大学生が有料スマホゲームを作った全てを公開するよ(2)開発環境とゲームの構成
の続き。
第一回はこちら。
大学生が有料スマホゲームを作った全てを公開するよ(1)イントロダクション

今回は、メインのゲームシーンについて。
いわゆる、インゲームと呼ばれる部分。
逆に、メニュー画面とかはアウトゲームって呼ばれる。
pertica(この記事で紹介するゲーム)の場合は、パズルアクションの仕組みを中心に解説していくよ。
パズルと言っても謎解きみたいな感じだから、もしかしたらみんなのイメージとは少し違うかもしれない。

ちょっとずつUnityも出てくる

ここから先は、ちょっとずつUnityの話になる。
プログラミングも出てくるし、
初めて見る人には結構難しいかもしれない。
Unityがどんなものかの説明も書いてるから、
なんとなーくでも理解してもらえると嬉しい。
後でUnityを使うようになったらまた見に来ても良いよ

Unityを知っている人や、ゲームを作った事がある人は参考にしてみて。
ボクはこうやってゲームを作ったよ。
この記事だと、後になるほど技術的になるから記事の下の方を見ると面白いかも。
これは前編で、後編はもっと技術的な話をするつもり。
アドバイスも待ってます!

主要なゲームオブジェクト

前回の記事でも触れたけど、ゲームシーンは大きく3つの機能を持っている。

  • メニューで選択したステージを生成する
  • ステージが遊べる
  • クリアしたら保存する

この3つだ。
そして、これを実現するために、
このゲームは10個の「部品」で出来ている。

pertica_Qiita説明.png

  1. プレイヤー [Player]
  2. ブロック [Blocks]
  3. 敵 [Enemies]
  4. アイテム [Items]
  5. その他のステージオブジェクト [Others]
  6. カメラ [Main Camera]
  7. ゲームマネージャー [GameController]
  8. デコードオブジェクト [DecodeObject]
  9. UI(リトライボタン、戻るボタン) [UI Canvas]
  10. BGM・SE [BGMSource, SESource]

これらの「部品」を今後は、「ゲームオブジェクト」と呼ぶことにするよ。
Unityの中でも同じ名前で呼ばれるんだ。
でも長いから短く「オブジェクト」って言う時があるかも。

ちなみに、[ ]の中はUnityの中での名前だ。
それぞれ説明するね。

プレイヤー

ゲームを遊ぶ人が操作するキャラクターだ。

  • タップした場所に弾を飛ばす
  • 弾が当たったオブジェクトと入れ替わる
  • 敵に触れたら死んでしまう

この3つの機能を持っているよ。

ブロック

基本的に四角くて動かないもの。
壁のこと。
マリオのブロックみたいな、四角くて動かないオブジェクトだ。

プレイヤーに当たると死んでしまう
プレイヤーに対して攻撃をしてくるオブジェクトだ。
マリオのクリボーとかノコノコと同じだ。

アイテム

壁を出したり消したりするスイッチとか、ワープホールとか。
プレイヤーが道具として使うもの。
壁と違って動く可能性があって、四角くない
マリオでいうと、キノコとかスターのことだ。

その他のステージオブジェクト

今説明した「プレイヤー、ブロック、敵、アイテム」の事を、ボクは「ステージオブジェクト」と呼ぶことにしたんだ。
でも背景の星とか、敵が出すレーザーなんかはその4つでは分類できない。
だからブロック、敵、アイテムに分けにくいものを、例外として分類する為に「その他」を作ったんだ。
これでステージオブジェクトは「プレイヤー、ブロック、敵、アイテム、その他」5種類になった。

カメラ

カメラはスマホの画面にゲームを映す機能をもってる。
テレビビデオカメラで撮影した映像を表示してるよね。
あれと同じで、ゲームの中の出来事を撮影してスマホの画面に表示してくれるんだ。
ズームしたり、カメラを複数使うことも出来るよ。

ゲームマネージャー

ゲーム全体の管理をするオブジェクトだ。
このゲーム(pertica)では、

  • リセット
  • メニューに戻る
  • ステージオブジェクトを作る
  • フェードイン

の4つの機能を担当している。

デコードオブジェクト

ゲームの最初にステージを設置する機能を持ってる。
perticaはパズルゲームだから、パズルのステージを用意しないといけない。
ステージはたくさん欲しいよね。
そしたらボクだけじゃなくて、友達にもステージを作ってもらった方が良さそうだ。
友達がステージを作れるように、ボクは友達に「ステージを作るツール」を渡す。
友達はステージを作ったら、「保存」ボタンを押す。
そうすると、作ったステージが「どこに何があるか」を文字にしたデータになるんだ。
でもデータでは遊ぶ事は出来ない。
遊ぶ時にはデータを実際にステージに変換しなきゃいけない。
それをするのが、このデコードオブジェクトなんだ。
ゲームが始まった瞬間に、「メニューで選んだステージのデータ」から「ステージ」を作ってくれる、とっても賢いヤツなんだ。

UI

リトライボタンとか、メニューに戻るボタンの事だ。
UIっていうのは、人がゲームを操作するために必要な仲介人なんだ。
「ゲームをもう一度やり直したい」とか、「メニューに戻りたい」と思うよね。
UIに頼めば、それを実際にやってくれるんだ。
頼む方法はボタンを押すだけだよ。

BGM・SE

を流す機能を持っている。
BGMはゲーム中にずっとなってる音楽
SEは「弾を飛ばす音」「スイッチを押す音」みたいな効果音の事だ。

実装方法

ゲームが何で出来てるかは何となく分かったかな?
部品が「10個」なのは多いと思った?
それとも少ないと感じたかな。
段々と技術的な話に入っていくよ。
面白くなかったら飛ばして、最後のコメントだけ見ても良いよ。
今回は、ちょっとした制作秘話も書いてみた。

ここからは、Unityを少し知っている人向けになると思う。
でもUnityを知らない人にも楽しんでもらいたいから、
簡単にUnityでのゲームの考え方を書いてみるね。

Unityの簡単な説明

_Unityの説明.png

Unityでは主要なゲームオブジェクトで書いたような部品の事をゲームオブジェクトと呼ぶって言ったね。
それぞれのゲームオブジェクトは、さらにコンポーネントと呼ばれるもっと「小さな部品」で出来てるんだ。

スクリーンショット 2019-01-22 13.46.46.png

例えば、ゲーム内のプレイヤー

  • 今いる座標を定める部品 [Transform]
  • 見た目を決める部品 [Particle System]
  • 物理挙動を制御する部品 [Rigidbody 2D]
  • 当たり判定を調べる部品 [Circle Collider 2D]
  • アニメーションを制御する部品 [Animator]
  • プレイヤーの動作(弾を飛ばす、移動するなど)を制御する部品 [Player(Script)]

みたいな小さな部品(コンポーネント)で出来ている。
このうち、上の5つはUnityが用意してくれてるんだ。
だから、ボクが作るのは一番下の一つだけ。
このゲーム特有の動きを決めるんだ。
を作ると言っても良い。
そして、この部品を作るためにプログラミングを使うんだよ。

[ ]の中はUnityの中での呼び方で、Player(Script)はボクが作ったんだ。
こういうプログラミングで、自分で作ったコンポーネントを今後は「スクリプト」と呼ぶよ。
Unityの考え方では、
の部品にタイヤがあって、タイヤの部品にネジがある」みたいに
ゲームの部品にプレイヤーがあって、プレイヤーの部品にPlayer(Script)」があるんだ。

少し退屈かもしれないけど、このゲームのUnityでの実装の仕方を書き残しておくね。

プロジェクトツリー

メインシーンの説明の前に、Unityのプロジェクトのファイルツリーを書いておくよ。

Assets
├─ Animation
├─ Data
├─ Image
├─ Material
├─ Music
├─ ParticleSystem
├─ Prefab
├─ Scene
└─ Script

主要なのはこんな感じ。
さらにResourcesって名前のフォルダを必要に応じてそれぞれのフォルダの下に作るんだ。

ゲームの仕組みを構造的に見る

ezgif.com-video-to-gif.gif

pertica_Qiita1.png

ここからゲームシーンの説明だ。
Unity内のメインのゲームシーンは、DecodeStageと呼ばれるシーン名で管理している。
このシーンが実行されると、まずDecodeObjectがデータフォルダに入っているデータをもとにステージを生成する。
次に、Main Cameraがゴールからプレイヤーに向かって移動
移動が完了すると、ゲームが始まる。
プレイヤーは、キャラクターを操作してゴールを目指す
ゴールにたどり着くとリザルトシーンに遷移してこのシーンの役目は終わり。
分かりにくいかもしれないけど、上の図を目で追って見て欲しい。

ここからは、10個ゲームオブジェクトについて実装の概要を説明するよ。

Unityの簡単な説明で書いたようにUnityは1つのゲームオブジェクトがたくさんのコンポーネントから出来ている。
前編の今回は、そのたくさんのコンポーネントのうちとても大事な「脳」の部分「スクリプト」を中心に解説するよ。
「スクリプト」プログラミングを使ってボクが作ったものだったね。

プレイヤー

pertica_Qiita_Player.png

プレイヤーの構造はこんな感じだ。
そしてプレイヤーの持つスクリプトは

Player.cs
void IdlingAnimation()
  踊るアニメーションを行うよ
  プレイヤーが一定時間何もしなかったときに使う。

public void Shift(GameObject obj)
  弾が当たったものと位置を入れ替える
  飛ばした弾が当たった時に使う

public void Die()
  プレイヤーが死ぬ時に使う
   ・死ぬ時のエフェクトを表示
   ・死んだ回数を保存
   ・プレイヤーのゲームオブジェクトを非アクティブにする
  みたいな事をするよ

public void Fire()
  タップした位置に弾を発射する
  画面をタップした時に使う

IEnumerator TypeAheadCoroutin()
  先行入力を行う
  弾のクールタイム中に画面がタップされた時に使う
  クールタイムの終了を待って弾を発射する準備をする

こんな感じ。
「void IdlingAnimation()」とか「public void Shift(GameObject obj)」とかはその下に書いてある機能の名前だと思ってくれたら良い。
プログラミングを触ったことが無い人は、分かりにくいよね。
プログラミングを触ったことがある人にとっては、関数の事だ。
日本語で書いてある説明を、UnityではC#っていう言葉を使って書くんだよ。

基本的に、先頭にpublicが書いてある関数は他のスクリプトから呼び出される。
書いていないのは、自身のUpdate関数から呼ばれる。
Update関数っていうのは、毎フレーム呼び出される関数のこと。
フレームっていうのは、ゲームの世界での時間みたいな感じだ。
フレームは1秒に60回くらい更新されるんだ。
だから、Updateも1秒に60回くらい呼び出される。
Updateの中でif文で条件を書いて関数を呼び出すんだ。

ブロック

iOS の画像 (1).png
iOS の画像 (2).png

ブロックは次の3種類だ。

通常の壁 [Wall]

四角いオブジェクトでどんなものがぶつかっても動かない。
プレイヤーの放つ弾が壁に当たると、弾が消滅する。

反射壁 [Reflection Wall]

四角いオブジェクトでどんなものがぶつかっても動かない。
プレイヤーの放つ弾が壁に当たると、弾が跳ね返る

スイッチ壁 [Switch Wall]

レーザーのような見た目をしている。
後で紹介するスイッチボタンによって、壁が出現している状態消えている状態の2つが切り替わる。
壁が出現している時の機能は通常の壁と一緒で、弾が当たるとその弾は消滅する。
壁が消えている状態では、壁の機能は発揮せずに弾はそのまま通過するよ。

さて、それぞれが持つスクリプトを説明するよ。
まずは通常の壁 [Wall]から。

Wall.cs

何もない。
実際は少しはあるんだけど、エフェクトを出すとか名前をつけるくらいの簡単なものだけだ。
通常の壁[Wall]はほとんどUnityの機能だけで出来ているんだ。
次は反射壁 [Reflection Wall]

ReflectionWall.cs

も、何も無い。
反射するはずの壁が、反射するスクリプトを持っていない。
実は、弾が反射するかどうかは壁ではなく、弾の方で処理しているんだ。

じゃあ最後、スイッチ壁 [Switch Wall]

SwitchWall.cs
public override void SetStats(int[] stats)
  初期状態で表示か非表示かを設定する。

public void SwitchEnabled()
  壁の表示非表示を切り替える。
  スイッチボタンの状態が切り替わった時に使う。

スイッチ壁はスイッチボタンが押されたときに、状態を変える。
でも壁の状態を切り替える処理自体は、スイッチ壁の機能として用意しておくんだ。

iOS の画像 (4).png

今のところ敵は2種類
名前はドッグ[Dog]ピッグ[Pig]だ。
名前の由来は...まぁそのうち話すよ...。
ドッグ直線移動して、壁に当たったら反対向きになる。
ピッグもそれは同じだけど、2つのピッグの間にはレーザーが出るんだ。
ドッグが持つスクリプトは、

Dog.cs
public override void SetStats(int[] stats)
  最初の状態を決定する。
  上下左右とその間の斜め、8種類のうち最初に移動する方向を設定する。

void OnCollisionEnter2D(Collision2D coll)
  他のゲームオブジェクトとぶつかった時に使う。
  ぶつかったゲームオブジェクトがプレイヤーだったらプレイヤーを倒す。

そして、ピッグはこれに加えて

Pig.cs
void SetLinePosition()
  レーザーの端っこの位置を決める。

壁に当たったら向きを変える処理は書いていないんだ。
そういうのは全部Unityにやってもらってる。

アイテム

iOS の画像.png

アイテムは5種類

  • キューブ [Cube]
  • クリアーホール [Clear Hole]
  • スイッチボタン [Switch Wall Button]
  • ワープホール [Dimension Crack]
  • シフト弾 [Shift Bullet]

キューブはその場で静止しているだけのゲームオブジェクト。
クリアーホールはその位置にプレイヤーが到着すると、ゲームクリアー。
要するに、ゴールの事だ。
ワープホール2つ1組で使う。
プレイヤーの飛ばすが片方のワープホールに入るともう片方のワープホールに瞬間移動するんだ。
シフト弾はプレイヤーが発射するの事だよ。

順番に見ていこう。

キューブ [Cube] のスクリプトは、

Cube.cs

何もない。
これもUnityのコンポーネントだけで出来てる。

クリアーホール [Clear Hole]が持つスクリプトは

ClearHole.cs
void OnTriggerEnter2D(Collider2D coll)
  データの保存、ゲームシーンの遷移をする。
  プレイヤーが同じ位置に来た時に使う。

クリアーした時のデータの保存もこのクリアーホールで行う。
保存専用のゲームオブジェクトがあるわけでは無いんだ。

スイッチボタン[Switch Wall Button]は、さっき紹介したスイッチ壁の状態を切り替える事が出来るんだ。
スイッチボタンに敵のドッグピッグがぶつかると、そのスイッチボタンと繋がってる壁の状態が変わる。
表示されてる壁は消えて、表示されてない壁が出現するんだ。

スイッチボタンはこんな感じのスクリプトを持ってる。

SwitchWallButton.cs
void Switch()
  繋がってるスイッチ壁の状態を切り替える。
  敵がぶつかった時に使う。

次はワープホール[Dimension Crack]

DimensionCrack.cs
void OnTriggerEnter2D(Collider2D coll)
  ステージオブジェクトがこのゲームオブジェクトに重なった時に使う。

void SetVisible(Vector3 viewpos)
  このゲームオブジェクトが現在カメラに写っているかの情報を更新する。  

void SetPairDC()
  現在このゲームオブジェクトと接続しているDimensionCrackを更新する。

ワープホールは開発中は、DimensionCrack(次元の亀裂)って呼んでる。
クリアーホールもシーンをワープする感覚だから、ワープホールって名前だと分かりにくかったんだ。
今回の説明では逆に、次元の亀裂って言ってもイメージ出来ないと思ったからワープホールって書いたよ。

最後はシフト弾[Shift Bullet]だ。
シフト弾はプレイヤーが発射するエネルギーの玉の事だ。
タップした方向に飛んでいって、「入れ替われるオブジェクト」にぶつかるとプレイヤーとぶつかったオブジェクトの位置を入れ替える
スクリプトを見てみよう。

ShiftBullet.cs
void OnCollisionEnter2D(Collision2D coll)
  何かとぶつかった時に使われる。
  ぶつかったオブジェクトが入れ替われるならプレイヤーの入れ替わる処理をする。

IEnumerator DieCoroutine()
  弾が発射されてからしばらくたつと弾を消滅させる。

public override Die()
  弾を消滅させる。
  弾が反射壁以外のものとぶつかった時に使う。

反射壁は反射する処理を書かないで、逆に壁に当たった時に弾が消える処理を書くことで実現してる。
反射する機能はUnityが用意してくれてるからボクはそれを上手に使うだけで良いんだ。

その他のステージオブジェクト

iOS の画像 (5).png

「プレイヤー, ブロック, 敵, アイテム, その他」をステージオブジェクトと呼ぶって言ったね。
その他のステージオブジェクトには、背景の星とかピッグのレーザー[PigLazer]とか予測線[Prediction Line]がある。
他にもあるけれど、それはこのゲームシーンでは登場しないから今度話すね。
背景の星は、Unityのコンポーネントだけで出来てる。
ピッグのレーザーは、

PigLazer.cs
void OnTriggerEnter2D(Collider2D coll)
  プレイヤーにぶつかった時にプレイヤーを倒す。

これだけのスクリプトを持ってる。
予測線は、画面を長押ししたときに弾が飛ぶ軌道赤い線で教えてくれる。

PredictionLine.cs
void SetLine()
  予測線の軌道を設定する。
  予測線は、プレイヤーからタップした方向にに直進する。
  予測線はものにあたるとその先までは貫通して表示されない。

こんな感じのスクリプトだけで出来てる。

カメラ [Main Camera]

perticaでのカメラは基本的にプレイヤーが画面の中心になるように追う事だ。
でもステージの始めは、ゴールを映して徐々にプレイヤーにカメラを動かす演出がいる。
プレイヤーが入れ替わるときにも、少し遅らせ気味にプレイヤーを追うように演出する。
これを実装するために、ボクはこんな感じにいくつかの状態(ステート)で考えたよ。
pertica_Qiita_Camera.png
持ってるスクリプトは

MoveCamera.cs
void SetState(CameraState state)
  状態(ステート)を切り替える関数

void LookClearHole_Move()
  ゴール(ClearHole)からプレイヤーの位置にカメラを移動させる。
  ゲームの開始時に使う。
  LookClearHoleという状態(ステート)のときに使われる。

void TracePlayer_Move()
  プレイヤーのカメラの中心に捉えるように、プレイヤーを追いかけ続ける。
  プレイヤーが静止しているときに使う。
  TracePlayerという状態(ステート)のときに使われる。

void GoToTarget_Move(GameObject objTarget, float speed)
  プレイヤーの方向にゆっくりと移動する。
  プレイヤーが入れ替わるときに使う。
  GoToTargetという状態(ステート)のときに使われる。

実はカメラの制御は結構難しい。
良い演出が思いついても、難しくて実現できてないものもあるんだ。
特に、ズームとかが入ってくると厄介で...。

ゲームマネージャー

ゲーム全体の管理をすると言ったね。
このオブジェクトはボクが作ったスクリプトだけで出来てる。

Main.cs
public void Reset(float WaitTime = 1)
  ステージをリセットする。
  ゲームシーンを最初から読み込み直す。
  リトライボタンを押したり、死んでしまったときに使うよ。

public void LoadMenu()
  ゲームシーンを終了して、メニューシーンに移動する。
  メニューボタンを押したときに使うよ。

public void FadeIn()
  ゲームシーンを終わるときに、画面全体を徐々に暗くするよ。
  ゲームをクリアーしたり、メニューに戻ったりするときに使う。

と、

Tutorial.cs
void Update()
  ゲームを初めてプレイする場合、チュートリアルを表示する。

主にこの2つのスクリプトだ。

初めてスクリプトを2つもったゲームオブジェクトが出てきたね。
ゲームオブジェクトは2つどころか、スクリプトいくつでも持てるんだ。

ゲーム全体を管理すると言ったけど、
このゲームでは、ゲームマネージャーの仕事は少なめだね。

デコードオブジェクト

Dataフォルダに入っている、StageCodeDataという名前のステージのデータを元に実際にステージを生成するよ。
データの仕組みや構造については別の回で説明するね。
デコードオブジェクトはスクリプトだけで出来ているよ。
ちなみに、これは先輩が作ってくれたんだ。

DecodeStageScript.cs
void Awake()
  メニュー画面で選択したステージのデータをロードする。
  データを解読(デコード)して、対応するゲームオブジェクトを次々に生成していく。
  生成したオブジェクトによっては、初期状態を指定する。

UI

UIは、ボタンの事だったね。
UIは基本的にスクリプトを持っていないんだ。
これは全部Unityが用意してくれているコンポーネントで出来ている。
ボタンを押すと、ゲームマネージャーが持ってるスクリプトの

Main.cs
public void Reset(float WaitTime = 1)

とか

Main.cs
public void LoadMenu()

を使うように設定できるんだ。

BGM・SE

音を鳴らす機能を持っていたね。
これは、スクリプトを持っているよ。
まず、SEは

SEPlayer.cs
public static void PlaySE(string keyname)
  指定したSEを再生するよ。
  流すSEは、事前に曲のファイルとキーワードを紐付ける必要がある。

そして、BGMは

BGMPlayer.cs
public static void FadeinBGM(string keyname,float pertime=0.008f)
  指定したBGMをフェードインしながら再生するよ。
  曲が変わるときの違和感をなくすために、曲の入りで徐々に音を大きくするんだ。
  流すBGMは、事前に曲のファイルとキーワードを紐付ける必要がある。

public static void FadeoutBGM(float pertime=0.009f)
  指定したBGMをフェードアウトしながら終了するよ。
  曲が変わるときの違和感をなくすために、曲の終わりで徐々に音を小さくするんだ。

大事なのはこんな感じだ。

例えばプレイヤーが球を発射するときは音が鳴る。
この時、SEPlayerのPlaySEを使うんだよ。

他にも、音を出したいときにはこのスクリプトを使って音を鳴らすんだ。
ちなみに、これも先輩が作ってくれたよ。

まとめ

ゲームは「10個の部品」で出来ていた。
さらに「10個の部品」はもっと「小さな部品」で出来ている。
Unityではこの「10個の部品」「ゲームオブジェクト」
「小さな部品」「コンポーネント」と呼ぶ。

「コンポーネント」はUnityが用意してくれているものと、自分で作るものがある。
自分で作るコンポーネントの事を「スクリプト」と呼ぶことにしたね。

「スクリプト」は大事な機能を持っていて「脳」みたいなものなんだ。
だから今回は、そんなスクリプトについて説明したよ。
実際には、パソコンが理解できるようにC#という言葉を使って書くんだ。
その言葉を書く作業がプログラミングだよ。

なんとなく分かったかな?

制作秘話

perticaの制作を初めて2ヶ月くらいたったある日。
帰り道でボクが唐突に言った。

「先輩、重力を消しましょう」

先輩「?????」

ボクが真顔で言うもんだから、ついに頭がおかしくなったかと思われた。

今のperticaは宇宙感のあるゲームで無重力だ。
でも昔は重力があったんだよ。

昔のperticaは入れ替わった直後に重力で下に落ちたんだ。
でもそれだと、画面の動きが目まぐるしくて落ち着いて遊べなかった。
そこで、perticaでは重力をなくした方が良いんじゃないかと思ったんだ。

先輩はオーケーしてくれて、それから「無重力計画」が始まった。
結構な変更だったから大変だったよ。
先輩もよく付き合ってくれた、本当に感謝してる。

無重力にしたのが良かったのか悪かったのかは分からない。
でも面白いゲームを作るために思い切って変えたことに後悔はしていないよ。

他にこんな話もある

メインシーンは「ゲームを作ろう」と思って最初に作り始めた部分だった。

「プレイヤー」「Dog」「通常の壁」「反射壁」「ClearHole」「ワープホール」「キューブ」「スイッチ壁」
これは機能だけなら最初の1ヶ月くらいで出来たんだ。
見た目とかは、適当な画像で代用してたけどね...。

実は最初の頃は「Cat」とか「Ojisan」とかもいたんだよ。
その辺は今後「デザインについて」の記事も書くからその時に話そうかな。
多分その記事は神回になるよ。

コメント

ここまで読んでくれてありがとう。

今回は、メインシーンについて触れた。
少し文量が多かったね...。

Unityを知らない人にも、ゲームの仕組みが分かるように書いてみたんだけど、どうだったかな?

今このシリーズ記事の全体の構成をぼんやりと考えてるんだけど、多分全部で10回くらいになると思う。

なるべくこのゲームを全部公開したいんだけど、どうすると上手く伝えられるかな。
もし、気になる事とか書いてほしい事があったらコメントしてね。

そろそろ実物があった方が分かりやすいんじゃないかな?
もし興味をもってくれたら、よろしくお願いします。
perticaは下のリンクからインストール出来るよ。

大学生が有料スマホゲームを作った全てを公開するよ(1)イントロダクション
大学生が有料スマホゲームを作った全てを公開するよ(2)開発環境とゲームの構成
大学生が有料スマホゲームを作った全てを公開するよ(3)ゲームの構造・パズルとアクションの仕組み(前編)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】木に衝突すると爆発のパーティクルを発生させ、プレイヤーを爆風で飛ばす

環境メモ

⭐️Mac OS Mojave バージョン10.14
⭐️Unity 2018.2.15f1

実際に動かした動画はこちら↓↓
https://twitter.com/nonnonkapibara/status/1089212339746877440

Qiitaの容量オーバーで画像が貼り付けられなくなりました。(errorとなる)

続きは、「はてなブログ」に記載してます↓
かぴばらさんの覚書ブログ nonkapibara

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Unity]Rxとasync/awaitの使い分けについて

UniRxでRxasync/awaitのどちらを使用すべきか迷うときってあると思います。
今回はケース別に同じ処理を書いてみてどちらのほうが書きやすいかについて検証したいと思います。

ちなみに、どちらを使うべきかという結論はでていて非同期ならasync/await、イベントならRxです。
これだけ聞いてピンとくる方なら特にこの記事を読む必要はないです。

Rx vs Coroutine vs async/await
もう結論が出ていて、async/await一本でOK、です。まずRxには複数の側面があって、代表的にはイベントと非同期。そのうち非同期はasync/awaitのほうがハンドリングが用意です。そしてコルーチンによるフレームベースの処理に関してはUniTask.DelayやYieldが解決しました。ので、コルーチン→出番減る, async/await → 非同期, Rx → イベント処理 というように分離されていくと思われます。

UniTask - Unity + async/awaitの完全でハイパフォーマンスな統合 より引用

ケース1

ボタンを押したときに何か処理をする。
ボタンは何回でも押せる。

実装

Rx

void Start()
{
    this.button.OnClickAsObservable()
        .Subscribe(_ =>
        {
            Debug.Log("Click!!!");
        })
        .AddTo(this);
}

async/await

void Start()
{
    var token = this.GetCancellationTokenOnDestroy();
    UniTask.Void(async () =>
    {
        var handler = this.button.GetAsyncClickEventHandler(token);
        while (true)
        {
            await handler.OnClickAsync();
            Debug.Log("Click!");
        }
    });
}

評価

これはイベント処理なのでRxが書きやすいです。
async/awaitで書くと処理が長くなります。

ケース2

ボタンを押したときに何か処理をする。
ボタンを押せるのは1回のみ。
シーン遷移などを想定したケース。

実装

Rx

void Start()
{
    this.button.OnClickAsObservable()
        .FirstOrDefault()
        .Subscribe(_ =>
        {
            Debug.Log("Click!!!");
        })
        .AddTo(this);
}

async/await

void Start()
{
    var token = this.GetCancellationTokenOnDestroy();
    UniTask.Void(async () =>
    {
        await this.button.OnClickAsync(token);
        Debug.Log("Click!");
    });
}

評価

これはイベント処理ですが個人的にはどちらでもいいレベルという感じです。
ケース1のasync/awaitではGetAsyncClickEventHandlerを使用していましたが、
一度しか待たないのでOnClickAsyncを直接使用しています。

ケース3

ボタンを押したときにWebサーバから情報を取得する。
ボタンは前回の実行結果を待たずに何回でも押せる。
ケース1の亜種で何か処理をするという部分が非同期処理になっています。

実装

Rx

void Start()
{
    this.button.OnClickAsObservable()
        .SelectMany(_ => Fetch("https://www.google.co.jp"))
        .Subscribe(body => Debug.Log(body))
        .AddTo(this);
}


// Observableなgetリクエスト
IObservable<string> Fetch(string uri)
{
    // ...
}

async/await

void Start()
{
    var token = this.GetCancellationTokenOnDestroy();
    UniTask.Void(async () =>
    {
        var handler = this.button.GetAsyncClickEventHandler(token);
        while (true)
        {
            await handler.OnClickAsync();
            UniTask.Void(async () =>
            {
                var body = await Fetch("https://www.google.co.jp", token);
                Debug.Log(body);
            });
        }
    });
}


// awaitableなgetリクエスト
async UniTask<string> Fetch(string uri, CancellationToken token)
{
    // ...
}

評価

イベント処理と非同期合わせたケースなのですが今回のケースではRxのほうがすっきりしているように見えます。
async/awaitではUniTask.Voidがネストしていて複雑に感じます。

ケース4

ボタンを押したときにWebサーバから情報を取得する。
ボタンは前回の実行結果が出た後にもう一度押せるようになる。
情報の取得中はモーダルビューを表示してボタンのクリックができない状態にする。

実装

Rx

void Start()
{
    this.button.OnClickAsObservable()
        .SelectMany(_ =>
        {
            ShowModal();
            return Fetch("https://www.google.co.jp");
        })
        .Subscribe(body =>
        {
            Debug.Log(body);
            HideModal();
        })
        .AddTo(this);
}

async/await

void Start()
{
    var token = this.GetCancellationTokenOnDestroy();
    UniTask.Void(async () =>
    {
        var handler = this.button.GetAsyncClickEventHandler(token);
        while (true)
        {
            await handler.OnClickAsync();
            ShowModal();
            var body = await Fetch("https://www.google.co.jp", token);
            Debug.Log(body);
            HideModal();
        }
    });
}

評価

ケース3とほぼ同じですがasync/awaitのほうは少しシンプルになっています。
どっちで書いても大差ない感じです。
厳密にいえばRx版とasync/await版で処理が違う(仮にモーダルが出てなかった場合、Rx版は連打すると何度も実行されてしまう)のですが許容範囲でしょう。

ケース5

ボタンを押したときにWebサーバから情報を取得する。
ボタンを押せるのは1回のみ。
情報の取得中はモーダルビューを表示してボタンのクリックができない状態にする。

実装

Rx

void Start()
{
    this.button.OnClickAsObservable()
        .FirstOrDefault()
        .SelectMany(_ =>
        {
            ShowModal();
            return Fetch("https://www.google.co.jp");
        })
        .Subscribe(body =>
        {
            Debug.Log(body);
            HideModal();
        })
        .AddTo(this);
}

async/await

void Start()
{
    var token = this.GetCancellationTokenOnDestroy();
    UniTask.Void(async () =>
    {
        await this.button.OnClickAsync(token);
        ShowModal();
        var body = await Fetch("https://www.google.co.jp", token);
        Debug.Log(body);
        HideModal();
    });
}

評価

Rxはケース5とほぼ同じですがasync/awaitはまたシンプルになりました。
これならasync/awaitで書くかなという感じです。

ケース6

ボタンが2つあり、1つ目のボタンを押すと1秒ごとに処理を行う処理を開始する。
1つ目のボタンを複数回押すと複数処理が走る。
2つ目のボタンを押すと1秒ごとに行う処理をすべて停止する。

実装

Rx

void Start()
{
    var id = 0;
    this.button.OnClickAsObservable()
        .SelectMany(_ =>
        {
            var _id = id++;
            return Observable.Interval(TimeSpan.FromSeconds(1f))
                .TakeUntil(this.button2.OnClickAsObservable())
                .Select(__ => _id);
        })
        .Subscribe(_id =>
        {
            Debug.Log($"{_id} task.");
        })
        .AddTo(this);
}

async/await

void Start()
{
    var id = 0;
    var token = this.GetCancellationTokenOnDestroy();
    var linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token);
    UniTask.Void(async () =>
    {
        while (true)
        {
            await this.button.OnClickAsync(token);
            var _id = id++;
            UniTask.Void(async () =>
            {
                while (true)
                {
                    await UniTask.Delay(1000, cancellationToken: linkedTokenSource.Token);
                    Debug.Log($"{_id} task.");
                }
            });
        }
    });

    UniTask.Void(async () =>
    {
        while (true)
        {
            await this.button2.OnClickAsync(token);
            linkedTokenSource.Cancel();
            linkedTokenSource.Dispose();
            linkedTokenSource = CancellationTokenSource.CreateLinkedTokenSource(token);
        }
    });
}

評価

実行中の処理を途中で止めたり加工したりするケースではRxのほうがシンプルに書けます。

ケース7

ボタンが3つあり、すべてのボタンを1度以上押すと処理をする。
処理が走った後は最初の状態に戻る。

実装

Rx

void Start()
{
    var version = 0;
    IObservable<int> Wrap(Button b) => b.OnClickAsObservable().Select(_ => version);

    Observable.CombineLatest(Wrap(this.button), Wrap(this.button2), Wrap(this.button3))
        .Where(xs => xs.All(x => x == version))
        .Subscribe(_ =>
        {
            Debug.Log("Click!");
            version++;
        });
}

async/await

void Start()
{
    var token = this.GetCancellationTokenOnDestroy();
    UniTask.Void(async () =>
    {
        var handler = this.button.GetAsyncClickEventHandler(token);
        var handler2 = this.button2.GetAsyncClickEventHandler(token);
        var handler3 = this.button3.GetAsyncClickEventHandler(token);

        while (true)
        {
            await UniTask.WhenAll(handler.OnClickAsync(), handler2.OnClickAsync(), handler3.OnClickAsync());
            Debug.Log("Click!");
        }
    });
}

評価

複数のタスクの完了を待ってから何か処理をするというケースではasync/awaitのほうがシンプルに書けます。

まとめ

最初にも書きましたが基本的には非同期ならasync/await、イベントならRxです。
完全にどちらかだけを使うのではなく状況に応じて最適な方法を選びましょう。

個人的な感想ですがRxは学習コストが高いことや最適な方法が分かりづらいこともあって難しいです。
それに比べてasync/awaitは従来の逐次処理とほとんど変わらないように見えるのでパッと見たときにわかりやすいです。
どちらで書いても同じような複雑さの場合はasync/awaitで書き、Rxのほうがシンプルかつ分かりやすい方法で書けるならそちらを使うと思います。
今回上げたケースの中でRx版はもっとこう書けるというのがあれば編集リクエストかコメントで教えてください。
(結論ありきで書いたのでもっと効率的な方法もあると思います。もっともそういうところがRxの難しさだと思いますが。)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む