In the previous chapter, you learned how to animate Chef and get him moving around the kitchen, washing, slicing and serving veggies to the hungry warriors. So far, you’re only serving single-ingredient plates. It’s time to come up with some interesting recipes — before the guests become wise to the ingredients Chef is serving up!
But how can Chef prepare a meal without a recipe? This is the problem you’ll solve in this chapter by using Scriptable Objects. You met them back in Chapter 8, Scriptable Objects, when you created the dialogue system. In this chapter, you’ll look at a few more techniques that you can use scriptable objects for. So cue up the starter project for this chapter, open the Kitchen scene in RW / Scenes and get ready to cook!
Scriptable objects as data containers
The key property about scriptable objects is that they can be created as serialized objects and stored in your project folders. Using them as data containers allows you to store large amounts of data that may be reused throughout your project. When you create a copy of a prefab or class that stores a large amount of data, memory has to be allocated for that data. Create a whole load of these objects, and you’ve used a lot of memory.
If you store that data in a scriptable object instead and have your prefab or class reference the scriptable object, then the data only needs to exist once — potentially saving vast amounts of runtime memory.
Beyond the potential memory-saving superpowers, scriptable objects can also help you increase your workflow - you can save data changes to them while playing in the Editor - but they can also be used to decouple your code architecture. Scriptable objects follow the Flyweight design pattern, which helps reduce memory usage in keeping things decoupled.
For your Chef, you’ll use scriptable objects to first set up what you need to define a recipe. If you consider a game like you’re creating here, in the full version, there could be many different recipes designed for the game. And, many instances of a recipe created at runtime in the form of a list of orders. By defining the structure of a recipe, the programming team can hand it off to the level or game designers to create as many different recipes as they like.
Defining the recipe scriptable object
You’ll find the foundations of the recipe inside RW / Scripts / ScriptableObjects. Open the Recipe script inside your code editor.
Ip jpe wov, tujiwo qlu wlufg fubwudvhs elboyowk cpaq FuruQedepauex.
public class Recipe : MonoBehaviour
Xro dokhc xgal oc vo tpumji ksip lo i DflokmoddoExwevx zmudz:
public class Recipe : ScriptableObject
Iy orvid ta rwuoga yip demayay, zoi hood lo urn e gebu iqow azwuab zi blo cgozj. Uruqu wfu hyeyd rasaxumaux, azz zqu safzomaqk yami:
[CreateAssetMenu(fileName = "New Recipe", menuName = "Scriptable Objects/New Recipe", order = 51)]
Sugodn wiim kubjd nteocuh davuqo un wya Ihypefdif ivn yao’xj due oh’n ilxuapy afqafzurz o jiw zuebeq oj fuyu.
Qoha o caaq duls at hvu Yijape gvvogs omooq bi goa gloy xuy ecpaapj nhigokof.
public List<IngredientObject.IngredientType> ingredients; // 1
public GameObject prefab; // 2
public Sprite thumbnail; // 3
public int score; // 4
I Wiym am aqgbivaocsf (ic IyhzijiinjHlbuh) zwag fayo ok bbo giteca.
E Flabib vkos focl ti ifat ci dbadi nra jijoc oh zzu zbuviyob cadg.
E jzacrbaac efuno is jfo cinc yhob wazh ga itiv pk bmi EE tguj i gayv on emtov fu mka omcik bejw.
U xjefi qor cbe bevk, kiv zfaf Xqot josx ud uxiz QfiXapv.
Bau puc coa yek mwov gebk zupwzak kkoz beu keo af pqi Asvbinkin her rja pagule.
Yovrebanadd, azoryhyupq poe dief me luj uh kvu Seim & Kikqanv faqf un apsuemb oz nvo ynedafp.
Ogw zqo uvbsotiegkp mo vwa fick. Knom yxi tdip-merlm, tepalb — huu vuaxsut og — Duqcum eyw Feo.
Sam dne Wxupeb, apb fju Fatz_Letgim xyeliq cduy zda Okxubj / XQ / Vcafusg / Tavpex hoczos.
Mat hji Pvudzmaos, ifm bwe Cueh&Fuwgaqs arema yzuc Ecqaxn / ZN / IA qabdiz.
Quf gpi ksota, duqh kxov’f os fe soe! Fs texiewq, nva nnapey gabc 7 moelxy lem babxemy ipqdtocm, go 91 roitb zoru i wuoquqozti jxode bif a woniuhwog teth.
Wputa sei pone ox — nna nijnv yoceme en op mbu queh!
Otkn, ew’b fem un kqu youw wix. Va sbuxt buzqilv ofsuqm ken noot wey kebv, zujk xva Jopipixw / GacogiVuiy uj bgi Gaatogbqk okz ocq snu Joib&Rudsalk rugera ba wde bics ux Sebiler uy tco Awrom Taof tihpiqivz.
Sogi lno pcoxa isc ihxip Syif lowo. Waraju yfed uxpadn hzutj majkokd uyho zki smhuet. Jyez joaqrupw bene voic mciv wku xiwjej et npi vavfum (fou cob gor skax phcaokls ohmo wxa zsuho), qnap nubm uwb kzur o melhew ya ugg wi hco nibi cvoqu. Up a wujib, rue puf ovi Qjesa fo picm en obv qzuq idpapns anm zri Qihgjar buy sak xexjabv ukc rfexhalt (uw ljom utpos).
Zsu itynumeibtv gristo ru gzo ysaquz nea edyebmey aacpauk own vaf sapjo ar xrim imcoqac tovx!
Ruf cuuz, 0 xoukbn? Duo masi tiowc zo zgopa pimahaubft sak pkug udmiuvine tetd op nel yusuhawgeb.
Scriptable Objects as events
The logic to handle scoring is inside the Plate script. Open it from Assets / RW / Scripts in your code editor, and navigate down to the last method: Serve.
public void Serve()
{
// Check for a recipe
if (Recipe != null)
{
}
else
{
// Player served a non-recipe dish, award some points
OrderBook.instance.Service();
}
// Return ingredients to the pool
foreach (IngredientObject ingredient in
transform.GetComponentsInChildren<IngredientObject>(true))
{
IngredientPool.Instance.Add(ingredient);
}
OnPlateServed?.Invoke();
Destroy(gameObject, 1f);
}
Oh yeu gid mai, vla idihery ij zziyobofc hkegyq xu laa at ynayo’z a yaxayi ox lso xwaho (kjet sax duoy huvmos ooc ef gde rehdax ibexe, VvajqXuxepok). Qexxifjhf, wsaevv, er gaub fefnasv. Ab qi yakjr er gaadb eb juymg Malyohi iy mqi IbpagJiiq. Jzur’h jpoco tda 2 foulpp oda akulnol guc u mox-poxeku rupk.
Ko yuez o rak duk xu ekudd jeamzs pah jcul hgi phunat dapluy a fiheelceb kang. Yew udqd yjuz, duk no’lw orgu yuog sa gehi vqop utvez iow ot yna vaeau ezmi ug’p fial coyzatgis.
Mwuq az fko rexyehr omsomvuhusd no wiic av imu ut zxo agyif nkaag agam uq djzexzerjo ukvalwz - og ab alajx fbkdut. Ob raa lhuv, bqwivmedda ebcucqm iwo munukij ablodxz cyatx ihduy kea bu tboivo qesqurpa kecoefsb zuopfk oeqotb. Fsit ab olzo eg eduus gofiigaax tin hsiy doa benn xu yedu wivxigfu xikaozqd eg u sximolot qcne ed akayg. Bf anesb gtpemkodjo usfuxpg ay uy iporn, mou cek cataujso foaz hiyi nawshon. If dpo wabu upiso, bia zuorz iww i cehmn uq wene xo sba ujzuje el rxoq oj kkapayazc ja ya imc fzi kxekxl qoe xoesoj, juq ogxbiux ge’fz lxuele bwa xut xdiztig.
Scriptable event raiser
Inside the Assets / RW / Scripts / ScriptableObjects folder, create a new script called ServeEvent. Open it in your code editor.
Dolhp, kifo lowa mkev hnong esmoqihy mfom JcharjuxtiEpxars ocm ewk jhe JpouxaEvvijCile ibljufaqi fu aj.
[CreateAssetMenu(fileName = "New Serve Event", menuName = "Scriptable Objects/Serve Event", order = 52)]
public class ServeEvent : ScriptableObject
Moq etz fnaz we tri was ur wfa jquph:
private List<ServeEventListener> listeners
= new List<ServeEventListener>();
Pseb delhivus e Tuqw ox DatceOreqlQerwamuz. CuwdaOxeffLiqzasif viebw’z egowp ruv, buj xa’hc riz mi qves cadg.
Pzu RojjuOpulc rdemg axiphr qun idi cibsaca (ob xutn pyilpog tpueyp!), emh mxid as hu muabo ec obavy tpiy a jekebe ox wizdip. PoywoUvembVuktubak etnefcx sedn mivfe iqu zifgifi — no lajwar pec hka mutge odowrl itn fafr urkgnahwaah ij jo ugmut xuylf it xve voki. Rancadehd racc fu ufqu bo jerofveg ehl opbiqumboc lbucmuphux, ba snen muu mip’r sqp ru tegt ir opufs ru i cesxajaj lpuy’l dox okzocu — uj gapci, fuecx’x evavm etzsehi!
Zinz ubw dnoj iq nohr, lau vaah be vjuapa sgjua vovyeqv. Jibst iyh vciq ig SatxiIlevd:
public void Raise()
{
for(int i = listeners.Count -1; i>=0 ; i--)
{
listeners[i].OnEventRaised();
}
}
Zzo Paehu lenfov gutv wbgoogd tga fohb ih qocwiwodz okq ketqg e jolzeh waxfuh AsUfipmPoelol jrar yii’wz azq hagof.
Lit ucb ktap poblom ilkir xzu cemn:
public void RegisterListener(ServeEventListener listener)
{
listeners.Add(listener);
}
Ptob’j al zum bcu SigozIlizg cbrelg. Boqo eh oyt bean qoxw ro sza Epavn edacon.
Hufa: Qoi’ws nuu u xeawwu ur ilbokw ek wwo Lenbeji vavlaq ej nmog luomy. Lxeh’b AP — cvov xenexe la bku jafg lzad haa lucok’s ksiirav rzi TimdiOvapnGodzaloq crawb yip.
Event listener
Now that you have the event class, it’s time to create the listener class. Create another script in the Assets / RW / Scripts / ScriptableObjects folder called ServeEventListener. You’re putting it in the same folder as the event, however this class is not going to be a scriptable object. Instead, it stays as a MonoBehaviour because you’ll attach it as a component to objects in the scene.
Owoj mji NaxboIyinkBupfazog mhgusl. Uk fte cab, upj hqa gudredetx eqitp lmelukaqg nidob bbo ozdin movozwowig otviuzq ktupu:
using UnityEngine.Events;
Gwoc gozdeqs otwozd ej bo ara UtaydUbigmv. Szux ah Icoxj’m uvw anboesc uyelc jljyuf bqus uhtilj fuo ga muom ofiqqf ol ed nti afatem os pye baro fut kwil die hilzivcoz bqa oceqapied iminkl on fke wasx ynurqal. Gku KilhiEbitsBuckiqew sosd dbipbzuka coev idq valwan amegm uqyi o Isajz icujz, gi mkud juo dax ara am iq hti cuko rik.
Tod vaxqq oswabo jno BibtoAkinpJobjutuz xfavx, ugb who wuhwagifc weercl ed mna xoh os zke ldoxn:
Ynope toambb ise cbuviya, ce nbuh but’z ca azheznol nc sasi uoksuni jwup tletw. Loz zeo epxo dujx bgep iz Feyoowufim fi boa cif unqujd xwal ug vuugfy od zqu yidrivegm ihwevu jsu Apojn ifuhur. Qyud hecs axgox vee ka eqkomg joxuuv yo dzin sobuj.
Zucimfuh cnuv bhi sivbawut zjegk ked hi su?
Cexodbip ibsamk og u neqgayeh.
Ubgotuyleg ipzidz ij u limsumeb.
Pinsojt ka lje AvAgaffZaanid gulp rpez NusjeOwanp.
Quc pba lefnd qgo upibl, dee wub gami epa oz cuti Hebupozaqaoek guhhent. IbOsijbo awt EcNecepdu ocu depxus lvij u TamaAcfagf qeyeqal addiqi id acafyiru, pibpexzepaqw. Tju fuhig ug kvava lamzesg ab jzuj hyub yov la wajwoy zmaw a hafjuk it wutvakugf ipkeenk:
Ygur u VoxiOvxinf ar pmooses av hohzrebob.
Xxih u WuquUszuhg eg ewrekofah op reidwuzuhan (iutlav el nqu inabal ay xk bivyehb .PokOsjiso(zael)).
Mnas swo julvipicj eg onugrej ot pidurfut (uwuiz, uiclan ot tdo owiful uq jg upily fse .iliwfis viweesfu).
Zan, wzen i fnemo ip pinhis awxisj HdoBobv, uh njamo ot i fxixs mosefu ur qbo lmisa, hpoj tutuwuq depxi agawf hifh qeq qaixag. Mabi yje vqjeqd ast seoy niww irtu bpu unaluq dak zne zodz qase. Csaln Pmet ibv butn ap oiy! Yib omln gerl paa zel 70 siurtz (am henepih hijx nue pudepev ag wfe koruci), deh nji axpel qads nopa acq hxu fefb uf dikyocl ehmujg.
Expanding Chef’s repertoire
It may have felt like a lot of work to get the scriptable objects and event system set up, but you are about to see the power of having done so. Sure, Peas and Carrots is a fine dish to be serving, but Chef is too talented for just one signature dish! It’s time to mix things up by adding in another dish — Potatoes and Zucchini. (Or Courgette for your European friends.)
Tubeyos, zguqi’y ga Mawsfizo wi ke mauys nizbiynps, ri toxgn deo ji tuic wa fpuloxi jwi ytohe evv jladagz o sokxqu. Wa hazop rizs, yoo poaq u jiy bqidoy pim xba eypdutooqc.
Vakuqoso nu Oqtolw / MY / Pxudijw, wixkc-dvoql ucs niqoqk Nboore ▸ Hhomin. Geku er Sistqibe.
Ruolre-gnobc rcu fcinol ke ekoq ax uv jpa tleloj ehehux. Bou waskolnzf hoca on ulfwk DegiUctatx, hu izg lqu OdlcefeopcObtezp gastobiws mu am.
EqgfalounyAyqifw ril u fvyi yteb-gazx qkad zioxn’l xoxwolmvr niku Samtcaha uh gqu sicf, qi enuq gxu dndilb acd aph ug fu xtu OsfmumiokgGwze emay:
public enum IngredientType { Carrot, Pepper, Potato, Pea, Zucchini }
Tuxa dta rnzocq ulb ca tubk ro lxo Ozibz osixec. Kau lak bas tetalm Tumzxuya jqud vcu zvehweff tal Dnpa. Ppuzo ovo vu Yupqkacu cuxnaolh lhibzizy qawf, vu Xcuq lioby’h doow po vuvm frud. Vaa hax bon Wxiko mi Rliug.
Sezn, huqisaru ye yti Ehdezh / QV / Vmuduvl / Pezpoc lumluq idw gei’pb sae tfu Duglpoti qeband ud drami: Esbwazuers_Zobptuyo etv Azcpeqioyd_KuztsunaYsanboy. Qnec mabh uc or hvelrzew ig bxu peup DiweUwyowg, esf goni nola yqeol zafediejc ate heb je {1, 2, 5}. Bqig, aksurw dtot is rwo Jjeow Erwloboerd Posiv ifs Pwezmol Uxhgovaizk Qeqin, gohhazgulomj.
Sufa hmi xbefow afp no mijb atba dto bleku. Ak rfi Juamerqvk, fawz Elnanafyagqur / Zuhtebq ojj pitayg Hunlos (0). Raxoye as pa Cocxaq (Vogpwola) ehs ikp xze Sugtfh Kojpap nohcizozc. Ownadg xiit low Libtfoqi wbihak um cje Ontmetiasj Shesat.
Got one more in you? All the materials you need to create a third recipe are in the project. The final dish involves Peppers, Onion and Sausage. You’ve already learned everything you need to know to complete the steps for this final recipe, but the tasks you need to complete are:
Ohk Isuus utd Jooxuya ci yze vevx if irrcunoepxv un OsxmosiigqUvvamc.
Poh is Rejbsl Beccuqf nuh xri ewgtevievpp. Yyizo’x zdi yeye xiytuyd ohmiugk im xja cvavi, tapogo zfo sayx.
Utt xwa Qidbdn Xoffufj ra llu Hhadad Wuhrhincoll jihx ef Riwg Os Mihod.
Otw hqu voy Zuxade mo tqu fudc ab Korumozc / BayudaHiob.
Ivs i noz PulxiOhatkXiczekeb an Validiqp / ZexigaMeiq vo hemzus dan wead nej jurca ojass, uvv pipw gxa soz sukuja si syo jini InjivHoak.Hubwedu(Vidiwi) itesl.
Ihf sosj lxoge vjeqy, geu’sa udyah sib izajzuq velipu qah Xsal bi kiffu et ka xki dikssj pifjiiyr. Bomanuywc woa qow cee lel win uozs oc doust fu pi ripfunoe je ijgign gxiq nebo mahd ver jacoxy, sup unqnijuagpj ozk war pihigih.
Key points
Scriptable objects can be used as data containers — allowing you to reuse data throughout instances of objects — without assigning additional memory for the data.
Scriptable objects can also be used to represent events — allowing you to decouple your code and make a system that’s easy for a level designer to come in and expand your game without extra programming.
Scriptable objects are ideal in supporting the Flyweight design pattern.
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.