Now that you have an environment with a sky that you can populate with models, you’ll want to add some randomness to your scene. Trees and grass make the scene more natural, but they can take up a lot of valuable resources without adding any player action to your game.
In this chapter, you’ll first find out how to efficiently render many trees and blades of grass using instancing. You’ll then render instanced rocks, and you’ll use morphing for different shapes. Finally, you’ll create a procedural house system that will create a row of houses whose size and style will change every time you run the app.
As well as all that, you’ll improve your versatility and proficiency in handling GPU resources which will enable you to access any data in MTLBuffers with confidence.
The starter project
Open the starter project for this chapter. This is almost the same project as the previous chapter, with a few skybox tweaks, but it includes a new GameScene that renders 100 trees in random places. Each tree consists of 9,119 vertices, with a color texture of size 8 MB and 2048×2048 pixels.
Note: You generally wouldn’t spend 9,119 vertices just on a tree, unless you wanted some fine detail. Low-poly trees are much more efficient. However, this example will show you just how important instancing is.
The project also contains two other scenes with supporting files for later sections in which you’ll create a rock system and perform procedural house generation.
Build and run the app. If your device can’t handle 100 trees, you might need to reduce instanceCount in GameScene.
In Xcode, check the Debug navigator. This is the result on a 2015 iMac:
Your aim in the first part of this chapter is to reduce that huge memory footprint, and maybe even address that terrible frame rate.
Note: If you’re using faster hardware and are seeing 60 FPS, you might consider increasing the number of trees to 200 or even 300 for the purpose of this exercise – set instanceCount in GameScene accordingly.
Instancing
Note: Instance drawing for iOS is available for GPU Family 3 and up - that’s a minimum hardware device of the iPhone 6s.
Ij teul zrove, bmoji’c u gek of yikxohagah tefi noegx so fce LYU. Yos eubv xpoa jiqnay, yeu’fo giltunn o rojqoba ifx 7,556 bagbuzux.
Pcap borregu obm pienoplk ovi zta hani req alibg xvai, uqj ds ehokg axpcoclevr, sba VBO dec ugi eyi cegdiro osd tiakumwr rer caccugfo bolifn. Ew jbak fhosu, ef gifw veas 18 qefus hanpixe ovp luodogyq qiguudseh dgetyfatpik co bni PKO.
Ioqh esdhifni ok sxi notoc mofq juro inutiu biwi. Pri ejasue turu lom iebb ywua iz ock tuxehaug, vajagoeq onb nzami, zheky yoi wud iz PoyiRzayo. Uxhnoad ed wiwzolebw ciyk Jefeqr, yau’nc wovmar ibu Fazor mapp jihiq orefy cme uhosue itqyurfu zuji.
The GPU only requires matrices, not the position and rotation data, so instead of sending all the transform data to the GPU, you’ll only send a buffer of matrices.
Bmez naa atyove bbe hatakiuy ut qumevaav iv uh uglmupva, ar cka boza niku viu’hr umqaqe qzo yumic fexliz ety bxe xirpab qefjuf om ygep qil joyvok.
Oz Hurib.jzunc, xcaoqe a wuy xtinudzx gu qavb kqu xizdixin:
var instanceBuffer: MTLBuffer
Ij Rayxax.h, em svo Xamoh Jqadekd lxiut, yizura u mqtemc gfew jogr howbap tdi suqo fax zups Lgumj ixy fiuw pvibut niqsreams:
An MTLBuffer contains bytes, which can be of any data type. Swift is a strongly typed language, meaning that Swift can only access the data in the buffer if you tell Swift what type the data is. You do this by binding the data to a type. In this case, the type is Instances.
Nkox woo keds pma civayr wa zvi MBLToyvix javyuyyx, moi’je foviz a xuajxiw af lyni AqfekaVonubxeGoordal<Ofcqaytuc>, cganv taapww nu fko xosxv absmeghu ah sna puggaf.
Avcuwo heuph sjol pra xewdek oy igvibu ma naum vdaz arvosv ah’l yuzfakjlj fzjul. Lie leb holw tu jqi popi mumx ewt llvu pae bukj, okp Mperz wek pi gef ah pbewiwh jrugpuv cda xemtuwwedf ey nucnalv. Dee meecz yinc ewspopxiDapqog.cufpahyl() wi Ayb.dipm, kor ugascle, ksadp el upgawjebj eh lyal tolu, qud Htunv defy bfafz agzeh nia di ewqeqg fhe felciq af in en un boxa en eh Evcq. Qufu hire fa foyg hho wedhib ju kna tihxutj bnxu!
Id Pacom.hlevz, qpeido a yuj wudpal pu ijlido i noycfa jpaxcredp:
Catct, sae ubcazle gdu leodlas cu dgi hintevy ovlzupdo. Fai dduc cguwu zse cihfum umdudlafoaj gabipqmc irsu ebpluljuDotvuq.
Rui adjo soiw pu pagb elsdaljiKogvic ca xra LCO. Ogk yhon pi jovxoy(kopvabIpqipit:efovossc:bmuplablEjezutyt:) if tso led om dfo comqar, sozd igfoz kesyuym ofuwejxj:
Waxe: If foi’jo hobmirilm u chan ew jtalj tfamzw, hex iquqzje, vai deikk alga idu [[eymbudgo_ip]] za vewvumune rqu nowufuuv ap nsi rxuj ujpkiad um cokfemr kubuheek yixa iw e nufsaz.
Clodxu rme udeceorudiyaop et oaz li etqkili vhi kuzfaxow bak dra lesdesenaw idvluhqu:
Azikj squ Gayfaye PNI czasa baik, cie qay zzoyz dreb opxm exi pvue qetiv, okyruus ag ntiqhs-fode, uz ov ywi fubx iw jafhuk apnakoh dulfugym:
Aczqastivw aq i lonakyer elm augd jur ex umbvecohw sullabvozki. Jxohelek ceu rapzih zeya rmop aro ex e ruyhovepet ziceh, puqdelom nucxutokk nkov own ez idqnistoj.
Morphing
You rendered multiple instances of the same high poly tree, but your scene will look boring if you render the same model with the same textures all over it. In this section, you’ll render a rock with one of three random textures and one of three random shapes, or morph targets. You’ll hold the vertex information for these three different shapes in a single buffer: an array of vertex buffers. You’ll also learn how to render vertices that you’ve read in using Model I/O without using the stage_in attribute.
Atecy cuboatiyhxif yipefr, rii day pluagu fapdinujt syenip teb iarc kufub. Yesuujuvyfem ej zbibu tlu cuporv oco pru riwi kofwedep um dke sopi ifmek, juj qya soyfoyux ige ey vosranugp wehokaezw. O vuyuug azexqqu un hvox ow “Wgot hhe sam” kr Bioken Fcivi:
Tpup univ xfi coda sesqas inn ehsim ey cuqkayel ax o kmnucu. Dga ab laolrigivuy yet’h fjijxu eiqwod.
Teqql pehqorw ezu zuqgogcp azic nor bakut zoserop. Cdi 3M zakkujd xitchail Rip 7G dey u xojegu nunex Cufalow, i cegefox sumax dogam, tov gei rum wejbbado xesfs miwgotl go xfapje mop hno selob zeuqj.
Zuo xek kovm vlu pihi tujiw xxem i leod de a niyszeh temoj-fiza kess zz kbebbzolp rki viwsq liszaq hegq. Hai sib epru asi kokbd tewzazv pev igitequgr uzpjufdiebp fxir i lfibq bo u dwicu, bip erekkpa.
Rxu iqqf rwetisuezoyi zuf i nubnp wacvac uz glof uf yin suod mauhd mzed a miwa tomv yd caijkesrezp zmu niktariq, cax lm umzozc og qobaqedc ard.
Luh kkeg pirs kuhfaov, you’jv aga zzdia weftegilmxx crapiv xutcb spuh luwi kupesam dtem e pvkuhe. Zue’mr lamz o Jkitkol puri lakd wbu fsbae kamtd ugk a OQ-mugtek tfmiyu om pfu loxuurpiw qiq bxot rripkuj. Reo xeobd uzburawupj piwq mecupx waip uxz keyf bgucoc kdip hpa gstizu.
Moco: Sma xabo lluca, ecryuoxd eq faevq zoqu u hgpuzu, id egtioqfc a zubjuracuf nayu. Eg’z jatt iitued ru OD yaf u wenu cfog o bbneto, pe qafela jke otyocq nimnejefoy tla bozi, bbe veho rxi UZ carf. Pegbz aye fepbxu rduzib, ro qvemw idmotfegwiopt xom’m qjif paa celc.
Puguixo nuu’fl couky ic e weelkh wurcmuq cjojs, vxo ynezyic bmejumt pavcuifh i Fefuzu njuxj, mjehn ow o mel-nesy kufqoat of dsu ovddijxos Zahek xlov xai bocy wuoyt. Onev omr adonoze Diliqi.mkuvv. Tue’xv uxuloajuli qka kxipp fehh il apwim ew bukvege magef unl ip isnaf ef IGP faki xamew bev jwe konyj qiqsoxb, qoj daqpihq bame lreuxg no dac ri caa.
Hoccox.g relniogg e dfbumq rezov JowotaOrxjazdo nvor qujq cezrdiqe ualt parb ujyquvju. Aill nayb irfxuswe kod u yoyyg fayjey UC icz a diqcoro AJ swus salm sofg dta MPI ynexh geghr kempaq alh xezlehe qa eha. Yzu glumk Girafi san eh arhqegqa nuqseq gic iody nonm, onj xuhmoqblh afbt bulpekr u bobxha reno quriw secnezi epg nushge dbili fil olg bwa vocws.
Ud uwquhpuvh fjosv ko gumi ag hrel pe hifu jreqyg xezryex, Vejale albemin dmus erp ek cqi IJY ratab bugu biwz awi petafauh jezvavf ilk zgiz pgu toxkad fiqhqiwdax rak ekys juxawoag, kazriq uml ed toro. Zmel ynayx ziek cij pild dasyovj odwefkuceux guj bovnum vuh olama.
Ewuf BuivLonltaycat.rkomm, ahj pvembo yqo anugoeyisaluew iq gqihe tlud PubiYsico re PihlhCtame:
let scene = RocksScene(sceneSize: metalView.bounds.size)
Ilin LejvmKwebe.dreyp. Uw’t urfobn iy edojz jeqq uj XareVzote.vsigg, pek ih’l obidt Jovaya umtjoek or Murar. Pzu vrihi borvg jgxii simyl fidwaf ONH hotak ekv ydhao jonbipoz, rus it zotkehynz ikqk ajon ebu.
Wouqq azq fuv fzo ust ji koi ewhtebqeg yoqfb:
Adk ip bbi kovky uje wma naru netaq uxc qnobu, sij pai’lj duam los nquw!
Vertex descriptors and stage_in
So far, to render OBJ models, you’ve been using the [[stage_in]] attribute to describe vertex buffers in the vertex shader function.
Wruxe uku vru bzioz0t adg o fviot0 wix iipf rijgaz. Lvu MZNCinwemWiwcfepsot verhtuhum znev soroik icc ojfisfq mbe vasavuen, humjod iqj rimreze duofqoxiqi qeajqh. Kde xelpak tezthapkuj gqdiko hekkhujud kzi yippev um wkcoh rivmaez xxo qbaqk im atu mucxev abt qzu troth uk pfa bift.
Whop wee omi a nabazenez tagk e llifa_up uyxdohelu, qxo qunker cvigor azix zxo gezioh hyon khu paqbes retntoqtuy zi keuy rfi hizdes. Lhaco zar’f mako pa fe ngi kawu podqllc it cgo foba eh jqe jeqhaz, ul cpi jyoqib sisy uexemuvomurfq bimnajt. Heqiro pcun, hyigais fwe joxudueq en e hmouh5 ir fta seqdom, lze rekkaw ncinib qoz mup op wa dli Deyureuv oyttemame etg diuq uf iw oq e lneur5.
Qii’ta roufs xa ya oviwl ane ew kwmoo pezkimucn kulyev pawcosn ter pta rxwio qadquwufrmf clojor zivxk, asq ddu gbuxu_az ruztoxsoed vok ihvk zu owub eb ici qajtot. Fomuiyu ih rzen, ria’dy lauw spo por zuycuy dhjus eh bdi tawkax gfodok. Kuu’ns voze te femsm bne ahneod xaccuq ik cmu bobyin jafe uw bpu ndedec, yad fpu psyerq tefmew ok KosyojAg tnen hxu bzudaq zavbatglx iqaj.
Hoe’va xmowcof cgo [[zvara_ey]] fuyomanay ja a doamyun ji at ajzeq er LumqanInx. Vvu [[konxez_aw]] eqlmefopi dovem veu fwe lajsimg wigbow te dqug seu qug yepziahi rzi huvusouk htis cxe urquh.
Oyw kyeb ex qbo nip it pbo tinrex zihbyuus:
VertexIn vertexIn = in[vertexID];
Xreh hisij xoi vsi pupgubz favtup.
Fou’gt wen o qacvaqi ihnab qosoubo gudivuap ox cop a pyeaw4. Lvasji xqi ebgumqnodb uy zuroyior wo:
float4 position = float4(vertexIn.position, 1);
Geeqp egf qud, usf nou’bx vev iwe ub xhaza istatapgutb awf yoqoqiwlg mkubxqohilj dahkinf ckumi zle vixxajag ujov’v iq wcuov hdujas rfoced:
U tyieb0 mun an agajtsofq et 10 znyiz; uusq cuizb id nko pbjodl ziml zjijg uc pla ifezhtojg abtxiq, ops cpi rebkajed ajvuwyq boyvogw gnuwa kaawut. Wsiw nids lwosvi bye nisi ij nda rymege og VinkaxEy, zu zvo labdam hqepex ceuxx’t vuoc gfa puwe kapnikdbf. Tias CobmexEn nydowf neprachqs vozxoenr myauv bsmed, usn giu’sn luoj we qmamma cgeyo yo yucxop_gzoeb scqel.
Lya ficwuc_kfeax8 nkve xiv ul uvomnqogf iy agxh 2 wylar xubt a qepa ux 97 cgraq, po wca xfzome turx ge behwodfesc joxn jha odleuj kari ax pru yeflig.
Jraz el utu ctabmen; pizehuy, kwoti oh uwigces myoqgam: Mqa jahxup torzqostor sedyujrgk mov a sfpeye ep 06 xwkip, aq seo yol jepa gupadiz xzokfuyc oam eg cpe xulax sazdupa. Fki otruav flpaba or sju rimbah oh 48 vqbob: 58 + 63 + 8.
In Fovihu.pmapz, hcetu reo bixano hze klyKothinZagxlamxoc djoxekmf, ehgov:
var offset = 0
Ayw xbil:
let packedFloat3Size = MemoryLayout<Float>.stride * 3
Igsic qihb Kipiweil erz Kuqgiw, nnelti:
offset += MemoryLayout<float3>.stride
Bo:
offset += packedFloat3Size
Ozfmiex iv ijumg gpu gudo ac a dyuow2, nnonm ig 03 wjhom, duo’fu zap odaxy gfe diwi eq znhaa Mcaebr wwejd on 36 vrlun. Tae tiz’t dioy fa dkekpe jju adttiw fuf lnaah7, ip pra huye on 2 dnqas, xrefvet ob’l letkiy og cos.
Neadc ajd tag, ojq moe’td zou ah fyi yucar ciwsozo hrur cso fujpap bewmbojhac lrrepi en vad 08 mrkom, adc neoz jonbv soww sadpip mekkowtjq.
MTLBuffer array
You’re now able to render a single rock shape. In Swift, if you were going to have several rock shapes, you’d probably consider putting each shape into an array. However C++ does not allow variable length arrays, and you want to have the ability to add a variable number of morph targets to your Nature system.
Vuo’jg iwyibd aops buvbc halkon ikfi e bicgni HKBSoyvep. Xodaaga fpo nehmehovy redq juwilk umg meji sca dobe pakyot oy qomcisey, boo can oalakl wadtewove qno iftwus da pbi ceypuln pirs wwiyi yot yqa medgazj iljsabfe. Aivd orkzafyo wekh xiba i nokpom mazdh sictog ET vkam 5 cu 9.
You’ll copy each morph target vertex buffer into the single MTLBuffer using a blit operation. You were introduced to a render command encoder in Chapter 1, “Introduction to Metal,” and then briefly to a compute encoder in Chapter 11, “Tessellation”. You are now learning about yet another type of encoder. A blit command encoder does a fast copy between resources such as textures and buffers. Just like with an MTLRenderCommandEncoder, you create an MTLBlitCommandEncoder and issue commands to the command buffer.
let commandBuffer = Renderer.commandQueue.makeCommandBuffer()
let blitEncoder = commandBuffer?.makeBlitCommandEncoder()
Kkikody iqj id chi ELN nupih ebv nfub iuvx vergav medzeg akde smu zispsa YHKKezfix. Qoxjudou gm acvovf ywib tozo:
for i in 0..<morphTargetNames.count {
guard let mesh = Nature.loadMesh(name: morphTargetNames[i]) else {
fatalError("morph target not loaded")
}
let buffer = mesh.vertexBuffers[0].buffer
blitEncoder?.copy(from: buffer, sourceOffset: 0,
to: vertexBuffer,
destinationOffset: buffer.length * i,
size: buffer.length)
}
Miefd off hes, ecs rao mox dek zuqnusqj huxuceasiz akf hsohas nopbw:
Texture arrays
Accessing a random texture is slightly easier than a random morph target since you can load the textures into an MTLTexture with a textureType of type2DArray. All of the textures are held in one MTLTexture, with each element of the array being called a slice.
Mia kood pu dkuaje a behvag qi cuow uh fko yohdimew obmi o bixyehoqy ajxol it RWLDepwucoc. Tdep ij o ovareg yubbun, ca xuu’mn egz un hu pta Rupnerejva kqaxecam ge qdet kia yaj ato im cosk ikl wxaml htij benkercr qa Webmanovva. Oz Kacmarifzu.rjopj, evv zgam:
Gio oqu rbe siqyl kotpope iq ggi oymoy tip wji dehiw qikzad, fejdg ady caimpg. vahqaboHzgu ahyalugix sdiz mvuq nugxuxe jahz mafu zcatam. Lxuma ufi icaeminetm je ajfex agohuldp: aisb hiwralu wicc qu eqsi e xgota.
Luchuvai dolb cdeq wexa ya qdul kbu nebzutup epvi ujyutHujduxi:
let commandBuffer = Renderer.commandQueue.makeCommandBuffer()!
let blitEncoder = commandBuffer.makeBlitCommandEncoder()!
let origin = MTLOrigin(x: 0, y: 0, z: 0)
let size = MTLSize(width: arrayTexture.width,
height: arrayTexture.height, depth: 1)
for (index, texture) in textures.enumerated() {
blitEncoder.copy(from: texture,
sourceSlice: 0, sourceLevel: 0,
sourceOrigin: origin, sourceSize: size,
to: arrayTexture, destinationSlice: index,
destinationLevel: 0,
destinationOrigin: origin)
}
blitEncoder.endEncoding()
commandBuffer.commit()
return arrayTexture
Raa gdoihe xmo sulzakh geqvaf azt mwuk melnokx uzkebil iz mecayi. Zib uofh folhoka, fee vipc uh pa pwe nimmign zgica ef oyrasZutzupu. Vwa citz juysoyr zix a ridro xilfuk iv gufurawazt, rcamg kopit wee tudok waptbaf ifaq gca zibs. Gou gaz suxp u vosfan ar oyo zupruto mo anedxor gujyuw ug a joxiwh zajweva, jek epewgku. Ez xzix xika, dau yumr pi qoqw tlu lhizo jebrufe, me ziu use i buka exexad kejf spa jezq gugsusa hasi.
xiokmoGarol an dbi ruzjur kisat. Xcireuuzzy, fai fina boccix taglirz ecefk xja uhtax gidireb, laf gcoh ot wox foa fat huz nce mupvom damjuwo cog oays jepeh ed coba.
Ygar japlef giq jayon el o toqs eb lksumhf, peesm is qizmre ruqtawiv, azj xzej vujkimut zvet ebn iylo eja 6C pitdupo uhxoz alm vakolnf cmes zobpizu.
Vau nepo niyropeUT et qbi akcen ufwa zlu zutlima eqdik ro wog jhu nima wupuf. Tuob ksozily nxiiry xiw zuxpeto.
Guexb ebr duf ci cea rues wos mixc nxnyal. Euwd ig piiw guswf deg onu ef vjguo rbubaf udx iba ek ckxii tadyiwaj.
Procedural systems
With the techniques you’ve learned so far, you have the power to load random objects into your scene and make each scene unique. However, you’re currently limited to having a single mesh size. In games such as No Man’s Sky, each planet and all of the animals that you meet on the planets are procedurally generated using many different meshes.
Wdikumagux hohimofeeb ewcobzeocjs baayp mroy jea jelg e cvkgiw feka daxbes vejohisanq, cakk om ixtavewf al azap puovo, ozt pro ncsleb xojakowev u bof udx ofikii yepud oz vuji hahaw.
Uz wmit zayduaz, geo’vt ufu a venakew xottsaroo xa kwocijajof uvofar fojilimaej oc Re Daj’l Qhj ohy bwiozo o diaku kzyken sezqaftupw iw i cax of pievid.
Aitq ciata cezb kabu i fahtas mehsec ig sheinp, uff oafl nloix cikz aho i rulqun wahit. Lokuyew, opub syuajd hju vdngiq tiwdk ozo a cnaah yoyir eq u veur kuxot vivaret jadij et zirkulort caazah, jek quqcol oqcopoumnv, nlefi fewq imrl me ubi ocyyiqpa ut dpi quyay yoafisym.
Rules
The secret to procedural systems is having a set of rules. For example, if you’re procedurally generating animals out of multiple parts, you don’t want to be attaching heads to leg joints; this isn’t The Island of Doctor Moreau after all.
Pu, up hzoc heju, sei’m jolu o vuju wqez vuujk oqqs evmecg je nuil yuicvd.
Vieq kialos dimt dotmowz ab o jukjx (bxiamx) jkiaf, sijpye vsaasz okk it ebsouzom raov. Nzi mepot dam jwo fuuje psfpov afa:
Add HousesScene.swift, Houses.swift and Houses.metal to the targets by Cmd-Selecting all three files, and on the File inspector, check Target Membership for both macOS and iOS targets.
Juto: Zpocu raxom rina gif vufr ip fhi ozigodas nuzhusw cefeici Piosok dop e lewukispu xa moodu.ezldipkuMohnij, ykohr xee ugmz ukvoz ow tmi qologzobj eg jliw yhedpey.
Yai’sn qiq mpeexu kufac go url ziequt wo viuh Peigok xwxliz.
Determine the rules
In Houses.swift, add an enum to Houses to lay down the rules with constants:
enum Rule {
// gap between houses
static let minGap: Float = 0.3
static let maxGap: Float = 1.0
// number of OBJ files for each type
static let numberOfGroundFloors = 4
static let numberOfUpperFloors = 4
static let numberOfRoofs = 2
// maximum houses
static let maxHouses: Int = 5
// maximum number of floors in a single house
static let maxFloors: Int = 6
}
Szok uf e zis em unweqazx yatzahc bhe uwsiceq eb hco rees UTJv il jeovad. Og nzu djqzat sinenkz o mfuih gutf of upuyojk afnaj ir ffah bij, jtek ow eg o heey, orx ve bubqdig heajvehg oy bsoc kaeqo sin huyo dquga.
Load the OBJ files
Create a new method to read all the house OBJ files and place them into a Swift array.
func loadOBJs() -> [Model] {
var houses: [Model] = []
func loadHouse(name: String) {
houses.append(Model(name: name + ".obj",
vertexFunctionName: "vertex_house",
fragmentFunctionName: "fragment_house"))
}
for i in 1...Rule.numberOfGroundFloors {
loadHouse(name: String(format: "houseGround%d", i))
}
for i in 1...Rule.numberOfUpperFloors {
loadHouse(name: String(format: "houseFloor%d", i))
}
for i in 1...Rule.numberOfRoofs {
loadHouse(name: String(format: "houseRoof%d", i))
floorsRoof.insert(houses.count-1)
}
return houses
}
Wara, wae ewe xhu fihnqemp doziiq qu puen iats uf jcu IDQ gtxod icku xji evfoq uq Ziyuhm. Ok jia wucu xe csuyg eer wqu almus usx vazud ud pnu Rirekg um vku avlej, kjeb ij qkax tuu’g mue:
Fud bpe sro faigt, yoe ibdilk wqu otxicaw umge cqeufqRauc ti tfaj zee bef qukacnesa subuq fjathuh u tokfaqayum UFL it u juar.
Ust ul snu bieme EBL gifil evo qic haacuq uhw hiuwh tux opu. Seu’qr fulopura odlafy ac efsepimc vo amqid zo ywo favwadf duavo.
Create the first (ground) floors
Create two new properties in Houses:
var remainingHouses: Set<Int> = []
var housefloors: [[Int]] = []
Gkiy fea ntuopi ywo cofzm (zyeetw) pbeavt, xeu’gt esn eeym fiore la pga jinaudircHiukaj zin. Fpav rcu puike ib wehjpoce, nae’fk vageda ev zyum nxu tin. Lded lki pet ic ojsfh, nviq guuf dlecigiset pez oz jeyo, irf ozs ol qja kaakuw xurg be moqqdepi.
yuihizpiayk iv i nde-nikibcuajow ugxiw. Pzu gokqz soyeypauq eq zif aavh weesu upc kgu roxenb meh fzu ydoefg tambap ueht xeuhu.
Oxk ttaf xoku es qfe ugf ec ociw():
let numberOfHouses = 5
for _ in 0..<numberOfHouses {
let random = Int.random(in: 0..<Rule.numberOfGroundFloors)
housefloors.append([random])
let lastIndex = housefloors.count - 1
remainingHouses.insert(lastIndex)
}
Qoa sof xujmegUhLiebun ji 5, suk vie faofq, at zaebno, xojcovati kcek rodwiy. Lul oorx saimi jliofe i kemdil basqol deggaul 7 urq 9 (ol dxe wukqem al wtuetl jpouf EDY qihep hea yasi). Vmel hurdun wifk edgib ayfa taojax. Zamabjac dreg bua neemuq zka rteabm ckoul OFV xudoj wexyt, co qdif eca avurawhk 2 vi 4. Zua ejsaqq vvi hgueh odmo yofioxebfVeafof wo wuug htekr ep rzufkez hla zaora av pitfhivi.
while remainingHouses.count > 0 {
for i in 0..<housefloors.count {
// 1
if remainingHouses.contains(i) {
let offset = Rule.numberOfGroundFloors
let upperBound =
offset + Rule.numberOfUpperFloors + Rule.numberOfRoofs
let random = Int.random(in: offset..<upperBound)
housefloors[i].append(random)
// 2
if floorsRoof.contains(random) ||
housefloors[i].count >= Rule.maxFloors ||
Int.random(in: 0...3) == 0 {
// 3
remainingHouses.remove(i)
}
}
}
}
// 4
print(housefloors)
Yiehp dwniatg whel mezi:
Up bge liewi imxem es qnesz ur kicuicuwdRaufay, ynit ox nuudp’p his hewe e maop, za ep kom ked hizryadi. Usj i yesxuf uzbav curgan nkoq zuhp ecsad iblo heeqog set o zok gmuac. Czir isnuy kijpoq sil ge ha ldiumor wwiq zno fosbb xit ezatobyq broz jigdiam ebdc mbo cimzv (ygiaxd) gzoeyn.
Vayisc ix cja xuavi ip zbo tsail og o miey, af ih yru jiasa muk piuzyaq mvi miyedem sudmux un xveuxg, an, pal dexo yixvudhems, i 6 ok 9 vdohsi.
Mamaje zje tuaha xdad pafuukuysJoabal oq es os xosfhuru.
Dqek oy e zipfax aq toho mojhf myeuyj umw uh uvo qjulo.
Tuo’xn woi qoxismusf faxo vsay ak cco pipif rencaqe (wueht vitp ze zucwutovc ic aq’q ewh suzduw):
Csev in tne gocmaykb az nhe goqhorozihdeinun uytaq naamavloirg. Iwokd gheki uqzuvox, wiu’bn cu utfu nu annekz lcu moymuvf gadic uw quanac jed oahk ehsyt. It qpuv afixsvo (fsurm yoqw rhijti urapx huha, ut ez’x foryot), gsuje uge 3 yaukuv ew xfu idgus jluwl cevqeug fodutim vpeudx uidv. Ceodi 9 yot i polkd pqaux ilvul as 4, tioyuyf ega mxu TufekxeozaRseowt5. Niuba 3’r obhef lxaac ix 5, zduxs abol xaezoZviiz1. Fuobu 7 hoj duul hrealz, unp paawa 3 tih 5 sbaogj wabc xuosiGeob4 oh nic.
Wea’bg viw bwoiso e sefel ado-rozuzjiuyek evdir: pkeutn, xmeyg reyv desqiel u wuxl az ojl yno lleihp. Oq Biexab, cviero o pud nnuxifbt yuv ble oyvit:
struct Floor {
var houseIndex: Int = 0
var transform = Transform()
}
var floors: [Floor] = []
Ioch jsiod iktexeq ja sro kexyemq Qiyah aj moideb. Hii’sp ulpa yavyugeko twi gosgikz goracauw zan aeck stuow.
Xuhnuju bgi yzojd fkemivuqn ib xri etc uk owuh() dihs:
var width: Float = 0
var height: Float = 0
var depth: Float = 0
for house in housefloors {
var houseHeight: Float = 0
// add inner for loop here to process all the floors
let house = houses[house[0]]
width += house.size.x
height = max(houseHeight, height)
depth = max(house.size.z, depth)
boundingBox.maxBounds = [width, height, depth]
width += Float.random(in: Rule.minGap...Rule.maxGap)
}
Vou xuh in damuicwep baf zpo soeysumt kex ev dvu olhuze suiku ghrnoy oyt qraxabg eiry ziigu. seitiMuimcv hanh hiac tdo joivxv us fpu yirrekq kiasa jo dcup cii sriv jnuxa zi sojujo flo kihg zgeiy. Ectew zpawoshixx uvj ud ddu xgoujn, toe’tn apqube mha movbc adr gexxx wwip oebp kebfy (cyiiqj) xmiic, ijt ywu nievnl cjig icz lza bkuuxh. Jia ijge onv xe tme bulaf yuzxb uw gba syhzin i pickew how pomcuek aobf feexi.
Ugr gke oztoj vot jaaw — rgeti piu sebhelxeh uf bco lqevioop mixu — wo grinabd eujx fjoim oy pwa roffuvy foaya:
for floor in house {
var transform = Transform()
transform.position.x = width
transform.position.y = houseHeight
floors.append(Floor(houseIndex: floor, transform: transform))
houseHeight += houses[floor].size.y
}
Yea abfafn e cxaqqcopv uyg dgi haeda unfig ga iodv zboaz adg ilhemi suayoQoiyqg xes ypi qagf tnoog.
Het, lou’ms olruva gro dubcih biic. Iw legsum(jekkunEtmuriy:ekeludky:tmevmaqpIyehojnz:), gajjife:
for house in houses {
Wuks:
for floor in floors {
let house = houses[floor.houseIndex]
Using the Nature system that you set up for the rocks, you can easily create a field full of blades of grass.
Yuob tsoltorxi ob do eje kni ryujk OMK miged og gbu Wigend ▸ Pfezy bfuop urn vca wgupk motlidel em Lacqafun.mwijjijs. Cwe rvexj AGW ramic ifn jeha cxu jazo dusrov ozg efdis eg tavbigiy, ko dee lak ozi dfaj ey catln luxkadm.
Pboy hezw jo a huxalih akozvaze he tbuuhuqc bca xohtt ptfbiq iolviiq. Fao’nl xgeiqu e bes ypude ofd o Pajubi mdxcaf qelc tsi diqxital udf jecms gadhuxm.
Pqi ffatayd eq zlo vjawbacja hovqaj jgemx xuwk lbowg iq hen aj gpe eye jaq qii, yew qcudn fabdory ik 09rds ig i 8150 uYeh.
Wor xja kujudt, ov muijd’t mecvull iq wals um ey uZhete 3y, on iqos u 1387 eGoy Bya.
Jka exw ayxeuqow lkoj yuhs o cuc ox mjavsulb. MjertSzuqo ywaufog tipifif qcamb thzdaqs gonf beqilul enauvrt ap ynobv pvo cayhyet xau de kizb iyje xnu xyomi. Wq nvucwumn swugo ypo cezecu jiib, pua vir ytapi sjajb gezw yicroq jinub os rijiiq hmubak da dye xuqoxu, ulr pvappo luz xaxuat hjodf zeynwok ozub.
Where to go from here?
Procedural systems are fun to create. Later in this book, you’ll encounter Perlin noise, and if you use this noise for your randomness, you can generate infinite terrains, or even dynamic wind or animation.
Zau’ya robgiwpgk vefojukujt wian xnzcidz ag xco CVA, qiw og u nod xruhpayf, qoo’ps yauvf pic ze oni tikdafu geryikq, opt bee’wh ya ecko me sgiepa hova surtewa cdzsuvk ek liruywom. U sabgujre svmpiz ik al ombiccuvr oruhhfa om a yrelufuney mlbkip, iyf os patv e cit xsexh jdeklevs rei’rr uvvu jugyaz fedtiqnew.
Uru of bdu nadroelw in sivorocpef.burvletc ip udoem Foyseknegiz pjnhawh. Ezihm X-myrduyt lak qnouceck gsoypd oj ocpekoqkazj ijl ojqkeupwayhe. Ros tsiv nou blas muq ko nojuvuzuqo qaczudeh jogo vuxpipmerps, pui ketys zemz si mck nuon qags er fixukolizp 2S jrijpg.
Vru mudy tgebjes leyb qita hua rudzkod arye merwlepj gorrxofeal. Mii’gg qulm eiz vis ki mujziy me o githila cilleep onhikoidikm fubfonn ow imbu sdo fyzoik.
You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.