Thursday, June 27, 2019

A Unity Editor Script To Automate Building To Windows, Mac, And Linux, And Then Zips Them Up

I set out to write a scipt that would allow me, with one click, to walk away from my computer and it would build my Unity project for Windows, Mac, and Linux, zip up the folders, and put them in my Dropbox folder that I will share with my secret play testers. I have done so.

I consulted the following blogs for an example of setting the BuildOptions and Player

https://www.blog.radiator.debacle.us/2015/09/scripting-unity-editor-to-automatically.html

and

https://www.gamasutra.com/blogs/EnriqueJGil/20160808/278440/Unity_Builds_Scripting_Basic_and_advanced_possibilities.php

My script in pseudo code:

[menu button attribute marker]
void BuildFlockOfDogsAndZipItUp ()
{
     DoBuild ("Windows");
     DoBuild ("Mac");
     DoBuild ("Linux");
     ClearFoldersFromDropboxFolder ();
     ZipNewBuildsAndMoveThemToDropboxFolder ();
}

I'll present the guts, then, if you're particularly interested, I'll talk through the journey!


NOTE: WARNING: LOOK AT THIS: If you're not used to writing editor scripts, make sure your script is in a folder in your Assets that is titled Editor.








Starting with the import directives / whatever they're called:


Next, basically, a translation of my pseudo code into C# syntax:


Let's go in order! What does FormatDate() do, you think? It formats the date the way I like it. (It's important that I save the dateStamp string so that I can successfully identify the build folders later when I'm zipping them up.) So here it is:



Next, the BuildPlayer() function is basically mimicking the things you have to/can do when you click File->Build from inside the Unity editor: And this is the function that takes forever, because it's where Unity actually builds the project. Each time it switches the active build target, it takes a while, and each time it actually builds the thing, it takes a while. So as you might have guessed, debugging this function is delicate and can take a while. So edit it with care: 



It's a cinch! So, next (if you scroll up and look back at the pseudo code), I want to move anything that's in my Dropbox folder out of the Dropbox folder:



And finally, the zipping function

fdsad fasf asd asf t wadg fsf aw
(THE SECRET TO THE ZIPPING WILL BE COVERED IN THE NEXT BLOG BC IT WAS TRICKY-WICKY-DO. THIS WON'T JUST work FOR MANY USERS)

dfskjl kj; joi jlkj poj 'lkj 'lkj' pj'oj 'lj 'j



Voila.

And for reference, the whole shebang, BuildFlockOfDogs.cs:


*******************************************************
**********************************************
***************************
********
***
*
***
*********
****************
**********************
******************
*********************
****************
***********
****
**********
************************************
**************************************************************
***********
******************************************
***
*

**
***

*
***************
********


Anyway. If you want to now hear about my struggle with zipping, read the next post!









  • [BONUS: QUICK SNIP TIP: (Secret: I'm just using the Windows snipping tool to get these code snippets and then dropping them in as .pngs instead of doing all the super tedious work arounds for formatting/embedding code snippets and sometimes i like to turn on all the formatting options and it's also like when you read something with strikethrough on, you feel like you found a secret maybe and if the feeling of finding a secret is different than actually finding a secret then i don't know what is.)][]}}}{}!!!!!!!!!!!!!!!!!!!!

Thursday, June 20, 2019

Maps of Flap of Daps

So a while ago I had procdeural level generation in Flock of Dogs. This was before I added networking and before I decided that I would draw tiles of a certain, very large size. Now I've got procedural level generation back in the game working with both systems. I'm using the same algorithm I used from before (and I don't remember where I found it). There's a million tutorials out there for random level generation now, and this one's fairly simple, not innovative, but it's effective and works for Flock of Dogs. Here's an outline of how it works:

1) Creates a 2-dimensional array

int width = 12;
int height = 14;
int[,] map = new int[width, height];


2) It iterates thru all elements and randomly assigns it a 0 or a 1, representing a tile that is clear and a tile that is a wall, respectively. At this point, you'd just get a map full of noise. Ther's one other condition: if the element represents an edge tile, (i.e. take an element map[x,y] when x or y is 0 or when x is equal to width - 1 or y is equal to height - 1), it will always fill it with a 1. This guarantees a solid border.

3) Then, it iterates through the whole map again. Starting with the first tile that is clear, it declares a new Room. A room is a collection of tiles that are touching and are clear. To define a new room, you recursively check each adajcent clear tile for their adjacent clear tiles, adding all of them to the same room. For this algorithm, diagonal tiles are not considered adjacent. After the first open tile has been processed and turned into a room, the algorithm continues on through the tiles, looking for more open tiles. When it finds the next one, before it creates a second room, now it has to check if that tile is part of an existing room. If it isn't part of a room, then it can create a new room.

4) After all rooms have been found, which, by the above method of creation are not connected to each other, it then proceeds to connect them all! It defines the first room as the Main Room. Then it uses a straight line drawing algorithm to set a path of tile spots map[x,y] to 0, for some collection of x's and y's that result in a straight-ish line (can look kinda like stairs, if it's diagonal) from the center of the Main Room to the next room. It then marks that next room as connectedToMain = true. I forgot to mention that a Room is defined not just by its collection of tiles that are open and adjacent, but also if it is connected to main!

4b) The straight line drawing algorithm just like checks the x,y coordinate of the center of Room A compared to the center of Room B, and increments or decrements x or y step by step, clearing each tile, until it reaches the center of the Room B. You can tweak this to adjust how wide you want the connecting passage to be.

5) Then it proceeds to each room and draws a straight line to other rooms until it hits a room that is marked connectedToMain. At which point, it marks all the rooms that it has drawn a line through as connectedToMain = true. And voila. All the rooms are connected now! Yayaya.

6) Define a Unity GameObject that has a Unity Grid, upon which it can create a Unity Tilemap, then iterate thru map and if an element equals 1, instantiate a mountain tile, otherwise leave it blank! The mountain tiles are 'smart tiles', by which I mean, whenever a new tile is added to their Tilemap, they check their list of rules (which I set up) and see which rule applies given their current neighbors and decide which mountain sprite (of the 64 mountain tiles sprties I've made) they should actually be (and its corresponding collision polygon).

*
**
********
*******************
**************************************
****************************************************


****************************************************
**************************************
*******************
********
**
*


Yaya. So, as you might've noticed, that gif already has the islands, clouds, and tetris pieces in place too. After I've generated the tile map, I have a big process for how I place all these things. There's sort of two ways. I either go thru each tile and do a thing or I decide how many things I want to do and then pick random tiles.

For islands, I go through the map again and for each tile that is clear. According to some spawn chance, I may or may not spawn an island in that tile. There's some nuance here for bigger islands that take up several tile spaces, and I'm probably going to revisit this bc I'm not crazy about the look of every island being exactly centered in a tile/the exact center of a 2x2 tiles/never more than 1 island per tile.

For tetris pieces and clouds, I don't iterate thru each tile, but rather start with a random number of objects I want to spawn, then randomly pick a tile an open tile, then randomly pick a position within that tile's boundaries amd place the object there. 

The clouds are generated as cloud groups, which comprise up to a few big clouds, then up to a few times some number of medium clouds, then up to a few times some number of medium clouds times some number of small clouds. If it a storm group, then they'll be rain clouds with random amounts of rain levels.

For placing trees, monster dens, and flower traps, since I happened to have a list of all the islands that have been spawned, and since each island happens to have an accessible array of permissable spawn points, I pick a random island, pick a random spawn point on it, and drop the corresponding object. I then remove that spot from the island's available spawn points.

So anyway, that's a lot of stuff. And there's going to be more. Shops, dams, air rivers, cave entrances/exits, fortresses, oases, beast lairs, villages/cities, whale smiths, kennels, inns, camp grounds, treehouses, a festival, race tracks, and more! I PINKY PROMISE NOTHING WILL BE CUT OR CHANGED FROM THIS PLAN EVER AND IN FACT I WILL ONLY ADD MORE.

So anyway, that's a lot of stuff. And performance, even in this mostly shaderless, mostly particle effectless game, does become an issue. So I create an ObjectsInATileHolder object! And whenever I spawn any of the non moving structures I've mentioned above, I associate them with an ObjectsInATileHolder. Then, if I want to 'turn off' a tile, I tell the corresponding ObjectsInATileHolder to 'turn off' all its objects, which, in Unity terms, just means setting them to inactive, which means that Unity will pay no attention to them in its core game loop. I use a coroutine to check once every second the location of the camera and find out which map tile it's over. If it is over a different tile than it was the second before, it then makes sure to turn on all its neighbor tiles (within a range I've currently set to 2) and makes sure any tiles that were on and that are not within 2 tiles, are turned off.

*
**
********
*******************
**************************************
****************************************************

****************************************************
**************************************
*******************
********
**
*

I generate all this using System.Random, which will reproduce the same 'random' set of values if given the same seed. So fo network synchronization, all I need to do is send that seed number to any connecting clients! (Assuming all the parameters match: map width, map height, room-connection-passage-width, chance to spawn an island, number of trees to spawn, number of clouds to spawn, number of tetris pieces to spawn, number of dens to spawn, etc.)

Monday, June 10, 2019

Arms of Flarms of Darms

A long time ago, I had 4 guns you could equip in Flock of Dogs. The shotgun, the sniper, the battle rifle, the assault rifle, and the pistol. Inspired by Halo, as placeholder weapons. You can see a floating machine gun sprite below:


Eventaully, I removed them from the game and limited players to only use what had been the pistol, but with an infinite clip, and then worked on other parts of the game. And over time, I've felt that I wanted to move the focus away from ranged combat for 3 reasons:

1. Ranged wants to keep their enemies across the screen from them, which means that if there's multiple dog riders targetting different enemies, it gets awkward for them to manage their screen space.

2. Visually very cluttered with everyone's projectiles flying everywhere.

3. Upgrades for ranged weapons usually means more projectiles, faster, bigger. These aggravate the above issues.


So anywayaya. I've made 4 weapons. They are a bow, flail, lance, and shield.



Arrow damage: 8 points
Knockback: 12 units of force
Range: 60 units of distance
Auto draw time: 0.33 s
Bow rotation speed: 720 degrees / s

The bow is fairly straightforward now. Initially, I had you manually draw the bow and struggled with the decision of what happens if you do not fully draw your bow: a weak attack or no attack? If a weak attack, how is that visually communicated? Does the bow automatically fire the direction you're aiming, or is there a bit of drag between your actual input and the rotation of the weapon? (that was a decision I had to make for the lance and shield too). Considering that you'll be doing lots and lots of shooting, it would simply be annoying to fail your shots. Which is, in fact, how pretty much every bow works in video games. The idea of a weaker attack, in the context of this game, seems difficult to communicate and unnecssary.


Flail ball damage: 9 points
Flail ball knockback: 8 units of force
Flail max angular velocity: 1080 degrees / s
Flail min angular velocity: 360 degrees / s
Time to increase angular velcoity from min to max: 2 s
Range: ~5.5 units of distance 


Of course, the first thought for a melee weapon was a sword, which I had actually made a few months ago as an upgrade for the mop, but that may get tossed at this point. I wanted one weapon that wouldn't actually require use of the 2nd thumbstick (aiming). This provides a lower skill floor and a kind of accessibility. The flail fit that perfectly. I experimented with slowing down your movement speed whilst flailing, but it's already somewhat challenging to close in on enemies considering the flail has the shortest range of the current weapons anyway. Plus, what's really the harm in allowing players to be constantly flailing? There may come a time when there's a cost to that, if I decide to implement weapon durability.



Lunge thrust damage: 20 points
Lunge knockback: 15 unity of force
Lunge range: 13 units of distance
Poke damage: 2 points
Poke knockback: 3 units of force
Poke range: 8.5 units of distance
Charge up time: 0.0625 s
Lunge duration: ~ 0.3 s
Poke duration: ~ 0.1 s
Lance rotation speed: 360 degrees / s

The lance delivers a lunge attack if your momentum in the direction of your attack is above a small threshold. Otherwise, it just does a poke. Both attacks can pierce (do damage to multiple enemies). Whereas with the bow I opted not to have a half charged attack option, delivering a well executed attack is everything with the lance and the damage it does matches that. The input is such that if you pull the trigger and hold it, you won't attack until you release. If you just pull and release immediately, your character will complete fully pulling back the lance, and then attacking (thrust/poke). I experimented with just requiring you to charge up the lance fully to do a lunge, and also not having two types of attacks, but rather you just fail to attack if you don't fully charge up. But the idea of flying to the right and then being able to to a lunge thrust backwards, against your momentum, doing full damage, upset the combat realist in me. Currently, I don't check your momentum until the lance has been fully 'charged' or brought back in preparation to strike, which takes like .0625 seconds. I may instead do the check at the time you start 'charging up' your attack, but then I'll hvae to handle the case where you choose to hold the lance in its charged up state. idk.


Time to expand shield: 0.1 s
Shield bash range: 2.5 units of distance
Shield knockback:  20 units of force
Shield holder speed reduction: (uses a air resistance formula based on the square of the velocity of the player)
Shield rotation speed: 1080 degrees / s
Shield expanded rotation speed: 90 degrees / s

The shield slows your movement when it's expanded. It also cannot be aimed as quickly when it is expanded. It also delivers a shield bash when it is expanded. Before I had the idea of expanding it, it was very simple, you just moved and aimed. I knew I wanted a shield bash, however, and I didn't want to involve a second button in weapon usage. I didn't want the player's movement to be permanently retarded by just simply having the shield, so the idea of 'wielding it' or 'holding it up' popped into my mind. So that became the unfurling of the shield.


Their visual polish is not complete and there are some systems (upgradability, durability, solar energizability) that I'm considering. At the moment, I'm more interesting in developing these 4 weapons, rather than making a large arsenal to choose from. These could take many forms. What I'm leaning towards is a small weapon rack on board the whale where you can swap out which weapon you're actively using. This would work similarly to how I had harpoons in the game previously, which I may bring back. So at any time, you can hold 1 weapon and 3 harpoons. Maybe. Or maybe harpoons act as another weapon. And maybe you can have 2 weapons at any time. I lean away from upgrades and prefer the idea of using the solar power as temporary power boosts to weapons that perhaps allow for their special attacks. I think this may be the place where I reincorporate my previous features of the replicator shield (a shield that triples your allies shots that pass thru it) and the electric tether (a rope attached between two players that electrocutes the enemy when they cross it).

By durability, I refer to the weapons being breakable/consummable. The idea of durability just ties into the greater design goal of symbiosis of whale, dog, and rider. The whale needs the riders to fight off attacks and the rider needs the whale to hold its weapon stores. However, issues arise when you're (a) out of weapons completely, (b) how to indicate a weapon's current durability hp, (c) when you swap out a half used up weapon for a fresh one at the weapon rack, (d) determining how each weapon loses its durability. I have some soultions for this, but maybe it will just be annoying to have your weapons wear out on you? There's this general concept of the dog and rider pit stop, which I believe is a very big part of the symbiosis experience I'm trying to design. The pit stop goes something like this: dog and rider have been flying around collecting resources, enganging in combat, landing on islands, whatever. Now the dog is low on water, perhaps its lost a few hearts, and the rider's weapon is broken, and there's a ruby the player has just found. So this is a perfect time for a pit stop. The rider can fly back to whale, dock the dog, walk the ruby up to the whale's mouth, grab a new weapon. The dog gets sprayed down, and fed, and takes a brief nap. And then the rider mounts back up and heads back in the blue skies.

Anyway. I'll talk about the dogs' hydration and food systems anoyther time. I've changed them slightly.


My favorite weapon is the lance! Yayaya!



Tuesday, February 5, 2019

Getting Good at Grass

Hello.

I've been working steadily on Flock of Dogs.

According to my standard for myself, I'm pretty happy with the amount of work I've done in the last few months. The online networking code works decently well all things considered. I held my 2nd online Flock of Dogs playtesting and while we found a bunch of bugs, I've fixed a bunch of them immediately after the playtest.

I've recently decided it's time to do a big, (probably) penultimate, art pass. Starting primarily with environment art, following in the style of the Dr. Seuss-like trees I've blogged about. Basically, using a sort of ink-imitation line art, filling sections witih solid colors and using hashing and contextual black lines for shading. I am going to be trying to get good at using my tablet, instead of the mouse, because that should pay off in productivity eventually. (I've used the mouse for the treehouse, the dogs, and the players, with some very rough pencil sketches that I uploaded iPhone photos of and then traced over). My tablet is small and cheap, but functional and I'm going to switch from using GIMP to Krita after spending some time looking into it. Krita has a good, new community, handles animation way better, and the aesthetics of it are much preferable. I even prefer its name and icon, which like is a sad subsittute for the tactile satisfaction of actual brushes, canvases, paint, or polymer clay and a knife. (Maybe I should get a mechanical keyboard......)

Here's the islands I drew (still using mouse in GIMP, but after this blog...tablet and Krita!):




For reference, here's a really old screenshot, a kinda old screenshot, and a brand new screenshot:



These most recent iterations on islands will probably be final art. I'll draw a bunch of them, and then they'll be procedurally dropped into levels. Or for hand made levels/areas, I'll pick which ones to use and/or draw custom ones. Same process for the other environments in the game.

I've removed Twitter and Instagram from my phone (after having removed dating apps some time before) and I'm happier for it. I'm going to try harder to blog and post on Twitter/Instagram in intentional, consistent ways. But I'm not going to beat myself up over it. I only beat myself up over losing in soccer and not flossing and not working on actual game development. I've been on a good streak in 2 of those things. The flossing is a struggle.

Peace out.

Monday, December 17, 2018

As Good A Time As Any

So when I've finished a feeature for my game, I'm usually tired and happy and I want to eat, sleep, go outside, play soccer, something, and not write a blog. If I'm in the middle of adding a feature, I don't want to blog bc I'm busy. And if I'm just starting a new feature, well then what's there to blog about? Which offers you this chilling challenge: to comment below on what stage of feature development I'm in right now!

Anyway.

So this will just be a general update from what I've done in the last month.

I have a playground arena where you can spawn clouds, monster dens, monster pods, empty rock dens, a wrench, gems, dogs, whales, whale equipment. There's two islands that you can land on and take off from and walk around on.

And I have the gauntlet mini-fake-campaign that has 3 levels that are each the same rectangle size with slightly different enemy spawns and one level has a fruit tree and you can fly your flock through the 3 levels and loop through them over and over until your flock meets its bitter end. Your health and water do not replenish, except for the fruit tree on the first of the three levels start with like 12 fruits, but doesn't replenish when you repeat the levels.

And this all kinda works over the internet!!!!!!!!!!!11
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!!!!!!!!!!11
!!!!
!

I say 'kinda', bc to test it, I just run two builds of the game simultaneous, enable 'run in background' and then tab between the running applications to play as two different users. Which is awkward and not a thorough way of testing.

I also haven't totally totally finished syncing everything over the network, like espcially if a player joins late, only some of the stuff will sync, but everything should maybe probably work fine if everyone starts together. And I'll confess that I had most of this stuff (that I have mostly working) done before Thanksgiving, which is when I said I'd share something playable. Of course, no one pestered me about failing to meet my promise, which was both a test of you and a test of me, to see how we both handled the psychological damage to our relationship due to the lame predictability of not meeting deadlines. But as I jsut said, I basically coulda met the deadline, I just didn't share it. That's partly because I didn't want to spend Thanksgiving weekend sending out a bunch of emails/Tweets/Instagram posts/Facebook posts/blog posts/Discord messages trying to get testers, or explain how to play, or fix the deluge of bugs, because I'm all about not overworking and really want to hone my ability to not overwwork and because I just wanted to eat and drink and do an escape room with old friends and then eat and drink some more and play Avalon.

But then after Thanksgiving, I got really into adding new enemy sounds and a dynamic enemy-crashing-into-island feature, which I'm very excited about and then I also got really into redoing the water system with using Unity's particle system's built-in collisions, and adding a 'wet skin' feature, and finally learning about Unity's tile map feature, which I may now swap out from my simple one.

Oh, and I also spent like a few days getting the hose's shadow to dynamicly match its position (because I couldn't just use a simple Unity sprite mask, since the hose uses a line renderer) and then beceause the hose can be held at various altitudes (at the standard whale/flying dog altitude or on the island's surface altitude) that meant the hose's shadow has to both show up on the island, which an appropriate offset and sometimes show up on the whale, with a much smaller offset. Anyway, it still doesn't seemlessly match the island's edges, but I'm still surviving without having to learn how to write shaders, which seems like an invesment worth doing..........later.

Also, other than the usual November proceedings of celebrating my birthday, veterans day, and Thanksgiving, in the last month, I went to a wedding in Newport without any single girls, a wedding in Nashville without any alcohol or dancing, and camping in Joshua Tree without a tent.

Hung out with my nieces, played some good and some bad soccer. Went to free yoga on the bluff in Long Beach.

Played Eclipse a few times.

So will I now share my game so people can test it?? Idk. But I'm done blogging!

Wednesday, October 31, 2018

Birthday Goal & Thanksigiving Goal

On my BIRTHDAY on November 10th, YOU can play the alpha of FoD Online TM (lol)! By which I mean, you can play maybe modes (1) and (2). On Thanksgiving Day, YOU can play (3) also.

Maybe...I hope.

These are the three planned modes:
(1) Playground 
(2) The treehouse demo (first level of game, offline only right now) 
(3) Gauntlet 


Playground: Basically an an open space where you can mess around alone or with friends. Spawn any items, spawn upgrades for the dogs and whale, spawn enemies to fight, reset the level, whatever. This is goal 1. This is basically my testing space for the interactions between all the stuff in the game. This would synced online. Players can join and quit. This is where I hope lots of bugs can be reproduced and thusly solved.

Treehouse demo: Basically the demo I've shown the past few months, which starts at the Treehouse and ends at the Air River. This mode will be offline only.

Gauntlet: And online version with up to 8 players. Start the whale off with 4 random station upgrades and 6 dogs. Then you play through 3 very basic levels of inreasing difficulty (read: just more enemies probably) over and over. The levels wouldn't spawn new health or upgrades. There would be a high score game, where after any level you can choose to "turn back", which means you have to play through the same number of levels you've already gone through, still with no new whale food or dog food. Your score would be some calculation like:

KILLS x 10 pts + UNEATEN DOG FOOD * 30 pts + GEMS * 50 pts + GOLD = SCORE

BBBbbbuuuttt.....if you successfully "turn back" and make it "home" you'd get like a 2x score bonus. So you can think of two runs that are basically identical. A team plays through 10 levels and then dies with a total of 2500 points. Another team plays thru 5, then "turns back" and makes it through 5 withi a total of score of (2500 x 2) = 5000 pts. Anyway. Just a little gamification!!!


*******************************

This will be my birthday present to us! I waffle between the self psychological management dilemma of (a) if you  tell people your goals, you're less likely to achieve them, because we get some sort of fake feedback as if we'd already achieved them, simply by telling people and (b) if I tell people my goals, I feel accountable and it can create a kind of deadline, which is motivating. I don't know what the future holds!!!!!!!!!!

Tuesday, October 30, 2018

BFIG and John Carmack Highlights

BFIG Highlights:

  • @turtleverse showing up at my booth before I realized the festival had begun and handing me his two votes and proceeding to play the demo.
  • Seeng two little kids who were terrible at my game and I thought were getting really frustrated and never got past the thorny vines north of the treehouse, but I never talked to them or tried to help them and they left. But like an hour later I saw one of them come back and give me her votes. 
  • Seeing a family of four who were terrible at my game and I thought they were getting really frustrated and didn't get past the thorny vines north of the treehouse, and I only talked to them once*. But like an hour later the mom came back and gave me several votes.
  • Having the 12 year old kid I used to mentor as part of Big Brother Little Brother program help me in the booth. Especially playing balloon soccer while everyone else was packing up their booths.
  • @turtleverse bringing back more of his friends to check out Flock of Dogs
  • Seeing a group of young adults play my game and thinking they had come as a friend group and then finding out they had just met each other and secretly hoping the guy would ask for the girl's number and somehow Flock of Dogs could be credited with bringing together two lovers, but I don't think it happened.
  • Chatting with the game devs next to me (makers of Hexile) and across from me (maker of Katie) and behind me (makers of Austen Translation) and down the aisle (makers of Skorcery).

*The time I helped them was because they revealed a bad design by me. They accidentally landed on the island of the thorny vines north of the treehouse. But they didn't know how they landed (holding A) and they didn't know to take off (also holding A). But once they accidentally took off again they were trapped between the thorns and the floating tetromino piece...so anyway. Bad design. I've since restructured where/when landing is taught/is possible.

Um. Yeah, who knows about the marketing value of going to events like this. But having this kind of deadline and getting to see people play my game is really cool. I could make a separate list of highlights for just being back in Boston, most of which would be playing with two of my old soccer teams and winning all my games and scoring some sweet goals and hanging out with old friends.

Anyway, I came across this quote from John Carmack. And I've bolded, italicized, and changed the text color of the part I found encouraging!:

I spent a lot of time last week at Oculus Connect giving advice to developers across the App Reviews, Start session, and hallway conversations.

Since we started, my reaction to the vast majority of mobile VR titles has been that they have fairly straightforward tactical quality and design points that have failed to be addressed.
Many of these are almost checklist things, and I have pointed a lot of them out over the various app reviews I have posted.

However, it is possible to check all the boxes and still wind up with a competently implemented game that just doesn’t have any soul.
I see a lot of games that are aimed at filling a slot — “a FPS”, “a strategy game”, “a puzzle game”, “a space game”, “a roller coaster”, and so on.
“Doing reps” with game development is an important part of growing your skill set, and generally a necessary step on the path to doing something important, but don’t be surprised when the project with all that time and effort poured into it vanishes without a trace in the market.

If you intend to do reps, plan and optimize your strategy around maximizing your experience gained while still producing something of modest value with little expectation of return. When you want to make an impact, I think the most important advice is:
Build something that at least some people LOVE.

Games are a matter of taste, which varies widely. Hitting on something that everyone thinks is fantastic is unlikely. If it turns out that you have made something that at least a few people are ecstatic about, even if lots of people think it is garbage, then you have a better kernel to grow from than something that is widely considered just ok.

For instance, I'll stand up for Daedalus and Thumper. Bait and Pet Lab aren't really to my taste, but I know people that do love them. There is definitely something there. On the other hand, there are hundreds of games on our store that have probably never gotten a single heartfelt customer recommendation.

The difference between something you use and something you love is the details, both engineering and design.

We have had some borderline-acrimonious discussions internally around “delight” — I argue that applications should be functional first, because delight doesn’t last, and often comes at the expense of efficient function. Games are different, and many can almost be viewed as essentially just a sequence of delightful interactions.

Watch your players very carefully as they play. The smile, grin, cheer, or even focused look of intensity is your signal to chase. Design inspiration may provide the initial points, but hard work iterating on it is how you hill-climb to the best version.

If you have even a few true fans, keep your project alive! VR is still very young, and most of the potential players of your game haven’t even thought about buying a headset yet. Land’s End was a great experience three years ago, and it is still a great experience today.
This is easy to screw up. I wanted to go back and add some things to the old Oculus Arcade project, but I found that it hadn’t been archived with all of the support libraries, and I wasted an afternoon trying (and failing) to get it building with current systems.