Jump to content
  • Advertisement

Blogs

 

Frogger - programming day 2

Just a quick update to show how my frogger challenge entry is coming along. I had a day off yesterday and have today put in collision detection and a few other things. You don't die yet if you drown and the crocs and turtles are going the wrong way, the collision detection has lots of bugs, but it is getting there. My plan is to first get a working version, then flesh it out as time allows. I will put in UI soon and work out how to do menus etc. Once it is playable and meets the challenge I will put in animation, improve backgrounds, cameras etc.  

lawnjelly

lawnjelly

Stateless Frustrations Frustratingly Stated (Handling Improperly Disconnected UDP Clients?)

So, after weeks of strange and seemingly intermittent/coincidental UDP server crashes(Receive Thread Halt/Un-catchable Exception) and the ensuing frustration that that causes when you aren't even trying to work on the server itself.  I sat down and spent a day trying to figure out the problem..  After an almost complete server rewrite from scratch with no real improvement in the problem.. I turned to random poking about on the internets.. Hmmm...  It seems like the problem only happens when I'm restarting the client.. Here's a post!! https://stackoverflow.com/questions/38191968/c-sharp-udp-an-existing-connection-was-forcibly-closed-by-the-remote-host And Here! https://social.msdn.microsoft.com/Forums/en-US/6b7aa353-259b-4637-b0ae-d4e550c13e38/c-udp-socket-exception-on-bad-disconnect?forum=netfxnetcom Yup, that's the problem for sure. It appears that even though UDP isn't supposed to care about the other ends state, apparently this isn't true if the state is Port Unreachable.  When the server attempts to receive data (BeginReceiveFrom/BeginReceive/EndRecieveFrom/EndReceive/etc..) from an async connection and the client has (Crashed/Quit/Etc.) an ICMP port unavailable packet is generated and this causes the UDP socket to generate an exception that (even if you try/catch the hell out of it) causes the async receive thread to halt(I assume, this is the effect anyhow). The best solution I've found, as stated in the link(s) above is to change the underlying IOControl behavior(Prevent the Exception from being thrown). //This value tells the underlying IO system to IGNORE the ICMP error. public const int SIO_UDP_CONNRESET = -1744830452; private Socket _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); _socket.IOControl((IOControlCode)SIO_UDP_CONNRESET, new byte[] { 0, 0, 0, 0 }, null); _socket.Bind(new IPEndPoint(IPAddress.Any, port)); The first line is the value that needs to be set, and the second to last line is how you can set it.  And these are the live bits of code that are WORKING without error in my server, Finally!  So, there, I've posted it again because it seems way more obscure than it should be. Enjoy.

Septopus

Septopus

 

Battle Gem Ponies DevLog #191 (Not a Single Thing Checked Off the List)

I am shocked at how quickly this Wednesday snuck up on me. Guess that's a good thing. Another week closer to my short term goals, but on the other hand there's still next to no progress being made on BGP. So caught up in the daily routine and so mentally drained after hours of office work, it's incredibly easy to understand how people fall into "the grind" trap you know? 

    Just wanna drag yourself home, flop down somewhere (preferably with pizza at hand) and just watch stuff, endlessly scroll social media, wish you were happier and doing awesome things, and fall asleep. But I gotta stay on track. Even if it's just 2-3 hours at a time. Even if these little tasks like"Just write 8 YouTube scripts already!" end up taking forever, just like every other small task self-assigned.

 Pretty sure I'm waist deep in burnout, but not in any particular situation to be able to aliviate that stress, so... Onward! Here's today's blog: https://www.yotesgames.com/2018/10/battle-gem-ponies-devlog-191-not-single.html

Yotes Games

Yotes Games

End of the Line Progress (10/10/2018)

"End of the Line" development has been at its fastest since the beginning of development, trying to reach its mid-late December Alpha release! Some amazing work has gone into the planning of such a complex story universe with many characters, groups, and locations all coming together to form one story. Expect the hints of the large mysteries involved in the series to start coming out around Halloween! Gameplay-wise, the game has received some major fixes and work on the code since the beginning of the month. Most of the voice actors planned have recorded their necessary lines so we're waiting on some last minute stragglers to finish up! It's very exciting to see all the effort that people are putting in for this game. A new story route is being finalized which will change the end drastically and allow for more choices to affect gameplay. The interactivity between the enemy and environment is being implemented as I type! It has been an amazing week for development and I can't wait to update all of you guys!

Plummet Studios

Plummet Studios

Full Speed Ahead!

NOTE: Guys at the time of writing this developer update I have made the decision not to rebrand the project. To anyone who has worked with me on development in the past, Aakrana has been around for over a decade. It's been a vehicle on a journey of learning, discovery and many great adventures. I had planned to relaunch production as Saryn: Kingdom of Thrones but really it was just a name. Just wanted to be completely transparent with what is going on. Aakrana and the gaming systems that were designed for it are back in play. Long live Aakrana. Sorry it’s been a while since I’ve updated the blog. The good news (and there is lots of it) is that the progress we’ve made on the servers in the last month and a half has been staggering. We went from basic account setups and some basic network code across multiple instances to integrating some of the most foundational systems to kick off alpha. In the last 4 weeks we have: -          Implemented our advanced questing engine -          Implemented tiers 1 through 5 of the resource harvesting system -          Itemized Resource harvesting for Lumbering, Herbalism, Farming and Mining -          Implemented phase 1 of crafting (Currently Blacksmithing) -          Implemented our first pass at day and night cycles -          Upgraded the starting area assets (more on this later) -          Improved the chat system -          Added personal banking and storage -          A bunch of gui improvements/functionality (In game compass bar, login screen updates, character selection updates) -          UMA character models are in game -          A lot more… All of the above has been fully integrated and is testing well on our headless servers. We are slightly ahead of our planning roadmap for pre-alpha completion by the end of November. I’ll attach a couple of screenshots to this blog for your enjoyment but I have a couple of messages I want to pass on at this point. 1)      If you are skilled in UMA character management, 3D modelling and or building/integrating 3D animations and want to talk about how you can be a part of what we are building I would really be interested in talking. Please head to our website and hit the opportunities link in the navigation bar to see more details 2)      If you are a 3D modelling professional and have fantasy RPG asset bundles for sale that are Unity and UMA compatible and are willing to offer us a an amazing “special offer” please get in touch. 3)      Are you a 2D fantasy artist and interested in showcasing your talents through our website and in game screens? We are currently so focused on delivering an awesome player experience with our in game systems that we could really use a talented 2D fantasy artist to help us spruce up our web presence. Things like loading screen artwork and GUI upgrades are all on our roadmap but if this is something that interests you we’d love to have a conversation. I would like to be clear (and the website has all of the details on this) this project is un-funded. Every cent that gets spent is money I am taking away from supporting my family. I am doing this project because I have a passion for game development and over 15 years working in the indie game dev world. We are looking for individuals with a similar passion and commitment who can work in a professional manner for the love of what they do. If this sounds like you please get in touch. Thank you for reading this latest blog.  Looking forward to the next one.

Slyxsith

Slyxsith

 

13 RONIN - DevLog #6 - Time for graphics

In my last blog post I showcased some of the audio I’ve been working with for my upcoming game 13 RONIN, now it’s time for graphics. But first a celebration. The 4th of October is a day dedicated to the cinnamon roll here in Sweden (yes, it’s true), which we, of course, celebrate by baking, buying and eating lot’s of (you guessed it) cinnamon rolls. Since I’m a fan of the pastry myself, I happily took part in the celebration.  But I also had another reason to celebrate. From October and till the end of the year I’ve reduced the number of hours I’m putting in on my day job from 40 to 32 in favor of spending more time working on 13 RONIN. Big kudos to my boss for approving this!  Main character How do you go about drawing a samurai with realistic proportions when you don’t know how to draw? I started by googling images on human proportions and created a reference image I could use as background layer while drawing.                            In parallel to this, I watched a lot of old samurai movies and Kendo-tournaments and with still images from these kind of videos as reference I drew my character. At the beginning of this project, I intended to use silhouette-looking sprites mostly drawn in solid black. This changed over time, I had a few different ideas, but finally settled on using gray-scaled sprites with very low-saturated colors for light and dark areas. I decided on this color scheme to keep consistent with the black and white samurai movie theme. The low saturated colors are there to make the sprites stand out from the background, but also to make the visuals more interesting. With this style, I’ve found a reasonable balance between aesthetics and time spent drawing. Background In the early days of this project, I was more into ninjas than samurais and the initial idea for the game was that you as a white dressed samurai had to fight a bunch of bad, bad ninjas dressed in colorful outfits. To get started I drew a forest background. Soon I discovered that even the best of the ninja-movies weren’t that good and I found myself switching to samurai-movies and my approach to the project changed. Now I wanted to create a fighting game looking like Limbo, with black ninjas emerging from the dark. Sometime later I re-discovered my love for low-resolution pixel-art and the Limbo-aesthetics was dropped. The code-project still carries the somewhat humorous working title of the ninja-project, it’s called “Ninja in the dark”. I wanted to start drawing something basic, something I thought I had the skills for and choose to draw a building which I later on turned into a tavern. It’s yet not finished and I can’t promise it will fit into the final game, but it will be the background of the first public build. Energy and score After finishing the background I took some time to draw the graphics for player score and energy symbols. I tried a few different ideas and settled on the score written in a custom font upon a scroll-like background. For energy symbols I also tried a few different ideas before stealing the yin-yang symbols from The way of the exploding fist and turning them red. I do love that game. In-game graphics There are a lot of animations to finish before the game is playable, but this is at least a taste of what will come. Please visit www.eraserheadstudio.com for more about 13 RONIN. Happy coding!
/jan.
       
 

MEASURING USER ENGAGEMENT: CHURN RATE

The opposite side of customer retention is customer attrition or churn. For app developers, churn means that users become inactive or leave an app sometime after the install. After all the cost and trouble of acquiring users, this is a highly unlucky outcome. To keep as many users as possible, it’s vital to understand what makes them stay and what makes them leave. The churn rate is a highly useful metric that helps to improve the app and make sure that the monetization strategy works. Why churn rate is so important? App developers tend to focus on app installs. However, a high number of new users doesn’t automatically guarantee great monetization results, if those users never open the app. User retention is the path to increase app revenue  and lifetime value of each user, plus it helps to decrease the overall cost of user acquisition. However, retaining users is just a part of the strategy. Developers should take specific steps to prevent them from leaving. Churn rate is the metric that can give valuable insight into the app performance. How many users leave per period of time   In which point of the user flow they churn Which bugs, crashes, inconsistencies in UX and app design make users leave Whether intrusive or irrelevant ads cause users to abandon an app If the onboarding strategy is working Whether the lack of features/game levels succeeds in retaining users long-term If an app fares well among its competition and manages to give value to its users Churn rate is a signal that can show you where to pay attention, to fix issues in your application. Having calculated churn rate, app developers and marketers access a wealth of valuable insights that should inform the monetization strategy. How to measure churn rate In the most basic terms, churn rate is the ratio of users that have abandoned the app to the number of users that continue with the app. This calculation serves as a basis for understanding how your app performs. However, the nature of user engagement is more complex. If you’d like to have insights into why users churn, you’ll have to dig deeper and use formulae that take into account more variables, than just acquisition and abandonment. The data that factors in can include the frequency of app usage, payment data, geolocation, customer segmentation data, etc. Overall, the more data you have, the more nuanced the insights are going to be. For predicting churn rate, it is possible to build a model with many variables. However, such a model can only offer a certain degree of reliability, usually, no more than 85 per cent. Simple formula There are many variations of the basic churn rate formula and many marketers like to develop their own calculations for their specific purposes. Here are the three variants of the simple formula of churn rate. Option 1 Source   Option 2 Source   Option 3 Source Cohort analysis The reality of user engagement is nuanced, as each user has a unique history of interactions with the app. However, to discover trends and their interdependence, app developers can segment users into groups and calculate churn rate for each group separately. Thus they can see how each cohort performs over time. Users can be grouped by the day of install or the acquisition channel or other characteristics – it is up to app developer to decide which hypothesis to test. Cohort analysis can be visualized in a graph like the one below. Source: CleverTap Making sense of churn rate Churn rate per se wouldn’t give you much information unless you have other data to compare it to. Naturally, some types of apps are built to support long-term user interactions while others are aimed at a shorter attention span. Therefore, what is normal for one app can be a worrying trend for another. The first thing to know about the churn rate is that it’s generally very high in relation to the general number of users. The majority of users will abandon your app directly after install. The average churn rate across all industries within 90 days is assessed between 71 per cent and 80 per cent. After 90 days, the churn rate is even higher, with only 4 per cent of users returning to app both on Android and iOS platforms. In fact, close to 80 per cent of daily active users churn within 3 days after install. Breaking down statistics by the app category, there are clear leaders. According to Statista, the lowest churn rate by day 1 is observed in the Hobbies, Games and Lifestyle categories. By day 30, the apps in Finance, Education, and Travel categories perform the best. At the same time, the lowest retention rate shows the apps in the Travel and Utility categories on day 1, and Games and Books apps by day 30. Source: Statista By the estimations of a 90-day usage period, apps in News, Communications, Sports and Weather categories are among the most sticky. Meanwhile, the biggest number of users abandon apps in Music, Personalization and Photo & Video categories. The comparison between apps is also imperfect. Keep in mind that the top apps retain up to 70 per cent of users within 3 days, while the next 5000 apps retain just 28 per cent. It’s a very distinct disparity, which illustrates how churn rate depends on the competition and the ability of the app developer to offer users either impeccable experience or some unique features. Conclusions Churn rate is one of the basic metrics for an app developer. Along with retention rate and LTV of a user, churn rate informs about the performance of the app and the monetization opportunities. Calculating churn rate is not enough – you have to understand what it means for your platform, category and app type. Then, with the data in hand, you’d be able to make informed decisions about optimizing your app for users. We're Clickky, a full-stack platform for mobile advertisers and publishers. We help app developers monetize mobile traffic and promote their apps to get more revenue. Learn more about us and what we do here , or if you'd like to work with us, let us know . 

Clickky

Clickky

 

BSP split plane determination

It's been a while since my last blog entry, but the problem I posed on my earlier blog entry still persists - how to efficiently choose a good split plane for an n-vector data structure. To summarize the structure, geographic points are stored as n-vectors (unit vectors) in a binary tree. Branch nodes of this tree define a plane that bisects/halves the unit sphere - one child of the branch contains points that are below the plane, the other child contains points that are above. Like all trees, leaf nodes are turned into branch nodes as they are filled beyond a threshold. That is the basic idea of it.   Split plane determination must occur when a leaf becomes a branch or when a branch becomes unbalanced. In either case, the method called to determine the split plane is agnostic as to why it was called - it simply receives a bunch of points that it must determine a good split for. My initial implementation was naive:   bestsplit = ... bestscore = infinite; for (i=0; i<numpoints; i++) { for (j=i+1; j<numpoints; j++) { split = (points[i]-points[j]).normalize(); score = 0; for (k=0; k<numpoints; k++) { dot = split.dot(points[k]); // accumulate some scoring heuristics, such as is each point above/below the split, how far above/below, etc. score += ...; } if ( bestscore > score ) { bestscore = score; bestsplit = split; } } } So basically - for all points, determine the point between it and every other point, normalize it, and consider this difference as the normal to the split plane, then test it to see how it "scores" using some heuristics. Probably you can see how this will perform miserably for any significant number of points. The big-O notation is something like n^3 which doesn't include the expensive normalization that is called n^2 times. Several other methods were tested, such as fixing the normal of the split plane to be perpendicular to the x, y, or z axis, but these also proved too expensive and/or also had test cases where the split determination was unsatisfactory.   Heuristics Enter calculus. If we can represent each of the heuristics as a mathematical function, we can determine when the function reaches what is called a "critical point". Specifically we are interested in the critical point that is the global maximum or minimum, depending on which heuristic it is for. So far we have three of these. 1. Similar Distance We don't want a split plane where, for example, all points above are nearby and all points below are far away. The points should be as evenly distributed as possible on either side. Given that the dot product of a plane normal and any other point is negative when the point is below the plane and positive when the point is above, and the absolute value of the dot product increases as distance to the plane increases, the sum of all dot products for a good split will be at or close to zero. If we let \(P\) be the array of points, \(N\) be the number of points in the array, and \(S\) the split plane, the following function will add all dot products: \(SumOfDots = \displaystyle\sum_{i=1}^{N} P_i \cdot S\) The summation here is not really part of a mathematical function, at least not one we can perform meaningful calculus on, since the calculation must be done in the code. We don't know ahead of time how many points there will be or what their values are, so the function should be agnostic in this regard. As written we cannot use it without inordinate complication, but consider that it is really doing this: \(SumOfDots = \displaystyle\sum_{i=1}^{N} P_{ix} * S_x + P_{iy} * S_y + P_{iz} * S_z\) This summation expanded will look something like: \(SumOfDots = P_{1x} * S_x + P_{1y} * S_y + P_{1z} * S_z + P_{2x} * S_x + P_{2y} * S_y + P_{2z} * S_z + P_{3x} * S_x + P_{3y} * S_y + P_{3z} * S_z + \cdots\) We can then rewrite this as: \(SumOfDots = S_x*(P_{1x} + P_{2x} + P_{3x} + \cdots) + S_y*(P_{1y} + P_{2y} + P_{3y} + \cdots) + S_z*(P_{1z} + P_{2z} + P_{3z} + \cdots)\) Or similarly: \(SumOfDots = S_x*(\displaystyle\sum_{i=1}^{N} P_{ix}) + S_y*(\displaystyle\sum_{i=1}^{N} P_{iy}) + S_z*(\displaystyle\sum_{i=1}^{N} P_{iz})\) As far as the mathematical function is concerned, the sums are constants and we can replace them with single characters to appear concise: \(A = \displaystyle\sum_{i=1}^{N} P_{ix}\) \(B = \displaystyle\sum_{i=1}^{N} P_{iy}\) \(C = \displaystyle\sum_{i=1}^{N} P_{iz}\) We can pre-calculate these in the code as such: double A = 0, B = 0, C = 0; for (i=0; i<N; i++) { A += points[i].x; B += points[i].y; C += points[i].z; } We will rewrite the function with these constants: \(SumOfDots = S_x*A + S_y*B + S_z*C\) This is great so far. We are interested in when this function reaches zero. To make it simpler, we can square it which makes the negative values positive, and then we become interested in when this function reaches a global minimum: \(SquaredSumOfDots = (S_x*A + S_y*B + S_z*C)^2\) So again, when this function reaches zero it means that the points on either side of the split plane - as denoted by \(S\) - are spread apart evenly. This does not mean that \(S\) is a good split plane overall, as the points could all lie on the plane, or some other undesirable condition occurs. For that we have other heuristics. As a final note, since the vector \(S\) represents a unit normal to a plane, any determination of it must be constrained to space of the unit sphere: \(S_x^2 + S_y^2 + S_z^2 = 1\)   2. Large Distance Practically speaking, the points will not be random and will originate from a grid of points or combination of grids. But random or not, if the points form a shape that is not equilateral - in other words if they form a rectangular shape instead of a square or an ellipse instead of a circle - the larger axis of the shape should be split so that child areas are not even less equilateral. To achieve this we can emphasize that the sum of the absolute value of all the dot products is large, meaning that the points are farther away from the split plane. To do this, we want to find the global maximum of a function that calculates the sum: \(SumOfAbsoluteDots=\displaystyle\sum_{i=1}^{N} |P_i \cdot S|\) Unfortunately there is no way, that I know of, to handle absolute values and still determine critical points, so we need to rewrite this function without the absolute value operator. Really all we are interested in is when this function reaches a maximum, so we can replace it with a square: \(SumOfSquaredDots=\displaystyle\sum_{i=1}^{N} (P_i \cdot S)^2\) Not unlike the previous heuristic function, we need to rewrite this so that \(S\) is not contained in the summation, and we can extract the constant values. If we skip some of the expanding and reducing the squared dot product we will arrive at this step: \(SumOfSquaredDots=(S_x^2*\displaystyle\sum_{i=1}^{N} P_{ix}^2) + (S_y^2*\displaystyle\sum_{i=1}^{N} P_{iy}^2) + (S_z^2*\displaystyle\sum_{i=1}^{N} P_{iz}^2) + (S_x * S_y * 2 * \displaystyle\sum_{i=1}^{N} P_{ix}*P_{iy})+ (S_x * S_z * 2 * \displaystyle\sum_{i=1}^{N} P_{ix}*P_{iz})+ (S_y * S_z * 2 * \displaystyle\sum_{i=1}^{N} P_{iy}*P_{iz})\) Again we will create some named constants to be concise: \(D = \displaystyle\sum_{i=1}^{N} P_{ix}^2\) \(E = \displaystyle\sum_{i=1}^{N} P_{iy}^2\) \(F = \displaystyle\sum_{i=1}^{N} P_{iz}^2\) \(G = 2 * \displaystyle\sum_{i=1}^{N} P_{ix}*P_{iy}\) \(H = 2 * \displaystyle\sum_{i=1}^{N} P_{ix}*P_{iz}\) \(I = 2 * \displaystyle\sum_{i=1}^{N} P_{iy}*P_{iz}\) As before, these can be pre-calculated: double D = 0, E = 0, F = 0, G = 0, H = 0, I = 0; for (i=0; i<N; i++) { D += points[i].x * points[i].x; E += points[i].y * points[i].y; F += points[i].z * points[i].z; G += points[i].x * points[i].y; H += points[i].x * points[i].z; I += points[i].y * points[i].z; } G *= 2.0; H *= 2.0; I *= 2.0; And then the function becomes: \(SumOfSquaredDots=(S_x^2*D) + (S_y^2*E) + (S_z^2*F) + (S_x * S_y * G)+ (S_x * S_z * H) + (S_y * S_z * I)\) Again when this function maximizes, the points are farthest away from the split plane, which is what we want. 3. Similar number of points A good split plane will also have the same number of points on either side. We can again use the dot product since it is negative for points below the plane and positive for points above. But we cannot simply sum the dot products themselves, since a large difference for a point on one side will cancel out several smaller differences on the other. To account for this we normalize the distance to be either +1 or -1: \(SumOfNormalizedDots=\displaystyle\sum_{i=1}^{N} \frac{P_i \cdot S}{\sqrt{(P_i \cdot S)^2}}\) Expanding this becomes: \(SumOfNormalizedDots=\displaystyle\sum_{i=1}^{N} \frac{P_{ix} * S_x + P_{iy} * S_y + P_{iz} * S_z}{\sqrt{(S_x^2*P_{ix}^2) + (S_y^2*P_{iy}^2) + (S_z^2*P_{iz}^2) + (S_x*S_y*2*P_{ix}*P_{iy})+ (S_x*S_z*2*P_{ix}*P_{iz})+ (S_y*S_z*2*P_{iy}*P_{iz})}}\) Unfortunately there is no way to reduce this function so that we can extract all \(S\) references out of the summation, and as with the previous heuristics, put all \(P\) references into constants that we pre-calculate and use to simplify the function. So at present, this heuristic is not able to be used. I am still working on it. The problem is that each iteration of the sum is dependent on the values of \(S\) in such a way that it cannot be extracted. Putting it all together What we ultimately want to do is combine the heuristic functions into one function, then use this one function find the critical point - either the global minimum or global maximum depending on how we combine them. The issue is that for \(SquaredSumOfDots\) we want the global minimum and for \(SumOfSquaredDots\) we want the global maximum. We can account for this by inverting the former so that we instead want the global maximum for it. The combined function then becomes: \(Combined = SumOfSquaredDots - SquaredSumOfDots\) Applying the terms from each function, we get: \(Combined = (S_x^2*D) + (S_y^2*E) + (S_z^2*F) + (S_x * S_y * G)+ (S_x * S_z * H) + (S_y * S_z * I) - (S_x*A + S_y*B + S_z*C)^2\) Expanding the square on the right side and combining some terms will give us: \(Combined = (S_x^2*(D-A^2)) + (S_y^2*(E - B^2)) + (S_z^2*(F - C^2)) + (S_x * S_y * (G - (2 * A * B)))+ (S_x * S_z * (H - (2 * A * C))) + (S_y * S_z * (I - (2 * B * C)))\) Again we can combine/pre-calculate some constants to simplify: \(J = D-A^2\) \(K = E-B^2\) \(L = F-C^2\) \(M = G - (2 * A * B)\) \(N = H - (2 * A * C)\) \(O = I - (2 * B * C)\) double J = D - (A * A); double K = E - (B * B); double L = F - (C * C); double M = G - (2 * A * B); double N = H - (2 * A * C); double O = I - (2 * B * C); And then apply these to the function: \(Combined = (S_x^2*J) + (S_y^2*K) + (S_z^2*L) + (S_x * S_y * M)+ (S_x * S_z * N) + (S_y * S_z * O)\) Because we are lazy, we then plug this into a tool that does the calculus for us. And this is where I am currently blocked on this issue, as I have found no program that can perform this computation. I've actually purchased Wolfram Mathematica and attempted it with the following command: Maximize[{((x^2)*j) + ((y^2)*k) + ((z^2)*l) + (x*y*m) + (x*z*n) + (y*z*o), ((x^2) + (y^2) + (z^2)) == 1}, {x, y, z}] After 5 or 6 days this had not finished and I had to restart the computer it was running on. I assumed that if it took that long, it would not complete in a reasonable amount of time. I will update this blog entry if I make any progress on this problem as well as the 3rd heuristic function.   Further Optimizations While I have not gotten this far yet, it may be ultimately necessary to emphasize (or de-emphasize) one heuristic over the others in order to further optimize the split determination. This could be done by simply multiplying each heuristic function by a scalar value to either increase or decrease it's emphases on the final result. The reason I haven't researched this yet is because if I cannot find the global maximum without these extra variables, I certainly cannot do it with them.

jorgander

jorgander

Quick Terrain Renders

I had some extra time this evening and wanted to do some terrain renders for fun. I know that a lot of people will use procedural tools to generate terrain layouts, but I wanted to toy around with just sculpting on a square with some brushes I created to make a few quick terrain renders. The first step was to use my custom brushes to sculpt out some landscape as shown below. This mesh is 1.5 million polygons I then had two lower poly versions made up because I wanted to see how low I could go and still keep a decent bake result.   The first test was at 90k polygons: I then took this mesh and hand painted it and rendered the following result: My next test was to see if I could go lower. I tried my mesh at 16k polygons. Once I baked, and hand painted everything I was able to get this result from rendering: I then took the render into Photoshop to add a more softer snow on top: The snow is pretty "rough" still but I didn't have a lot of time left today. I'm pretty happy with the bake and render though for my 16k poly mesh. Anyhow! This was just a little thing I wanted to do for fun this evening. Thanks for stopping by!  

Rutin

Rutin

 

Mobile Update version 1.6 Change log; Submit to Apple Tomorrow 10/9

This update is huge. Lots added which is why it took over 2 weeks. It’s very epic and I’m extremely proud of myself with this update. I hope you all find it fun!!! Here are the changes Change log Version 1.6 Game Mode Update:  2 New Ways To Play the Game! 2 Brand New Game Modes!!  Completely change the way you play the game.  Offers much more replay-ability and different ways to enjoy the game.   All Game Modes can be permanently unlocked for 100 coins in the Store (Pharmacy).   “Roll and Throw” Game-mode Play the original game but using Chemo Balloons as your main weapon.   Instead of the Baseball Bat you will have Water Balloons filled with Chemo Medicine. Offers a completely different play style as you can attack from a distance with this “throwing” mechanic. All new animations for character and chemo balloon along with new sounds!   Germs and Boss’s have adjusted health in this mode, this is for difficulty balancing.  Everything is exactly the same except for the way you attack.  “Coin Rush” Game-mode: In this mode you can not attack.  The only power up you can use is the “Beanie” the ability to roll. Sarah is in a rush so you are now running and roll faster.  There will be no enemies in this mode. There will be a 60 Second time at the start of the game.  You will move room to room, all chosen randomly, trying to collect as many coins as you can before the timer reaches 0.  Sound Easy?  Well it won’t be as you will have to dodge all the room hazards in each room.  Spikes, Mucus, Crates, Fireballs, and holes in the floor are all there to stop you.  Also the coins disappear if you do not pick them up quick enough.   All Coins will be placed randomly and have random timers on them when they disappear.  They will give a blinking animation to alert they are going to disappear. Find Timer Pickups that will add seconds to your timer! Can you Top the leaderboards for this game mode?  That’s right an all new leaderboard!! Brand New Music Track for this game mode.  Music by Luis! You can now play Endless Mode with either weapon!!  When you select Endless Mode it will ask you if you want to use the Bat or Chemo Balloon!! New Save Game Reminder Pop Up feature.  If you click on Story Mode or Roll and Throw mode and an auto save is found it will alert you to either start new game or load your save game.  Starting new game will overwrite the auto save data. New Section In SOSopedia for explanation and description on the New Game Modes! Credits screen updated and Added More Stats to the stats page Text in Store (pharmacy) updated for unlocking all Game Modes.  New select cursor when using a controller.  It is now animated and in color!  Also updated for all added buttons and screens.  Made the Coin, full heart, and half a heart pickups slightly larger. Made large hole look more like a hole and allows you to see the edge clearly.  Now watch out and don’t fall in!   Dryers no longer have a delay before they start shooting fireballs when you enter a room.  They will start firing now right away.        Fixed a crucial bug that could have prevented the defeat of chapter 2 boss, Chronic. Fixed another crucial bug that could possibly cause the screen to stay white after beating Chronic in Endless mode.  Bug Fixes and Optimizations Done!   View the full article  

SOS-CC

SOS-CC

Tales of Vastor - Progress #9

Tales of Vastor - Progress #9 Content What's done? What's next? Music What's done? Black knight model Another enemy was added to the game - the black knight, one of the late game enemies. Waypoint icons The waypoint icons will help you identify what is waiting on a certain waypoint. Currently, the following ones are designed and configured: The boss icon will be displayed on waypoints, where a boss will occur. So, you might want to buy a few items before getting into the fight. The merchant is located near Udins castle. There, you should stock up a few potions or even buy a new weapon. Last but no least, the temple icon. It displays a temple, where you can save the game and heal all your characters. Status icons Up until now, there was no way to know, which status effects applied to a certain character. Later, the following icons will be displayed, depending on the active statuses. A short explanation to the effects: freeze: Does not allow action choosing for a certain amount or turns. The only good thing about the freeze effect is, that it nullifies the fire effect. fire: Damages the affected character at the start of each turn. If you can not remove the status fast enough, the character might end up dying. If the character is frozen, the fire effect will remove the freeze status. defense up: Raises the defense of the affected character. This status effect is essential to survive certain bosses or even harder fights. lower defense: Will decrease the affected characters defense. You should be careful, if a character has this status applied, because the damage received will increase. New background Here is one of the refactored backgrounds used for fights: This one provides more fog in the background, which shows the depth of the landscape. Lately, I love to put in a little bit of fog to the backgrounds, since it provides so much more life. What's next? This week, I want to focus on the character animations for the playable characters. Up until now, the base animations are already available for every character. Power attack animations, on the other hand, are still missing. They should be complete, as soon as the beta version is released. Alongside with the new animations, a few particle effects have to the created, such as lightnings, status changes and many others. Music I am glad to tell, that the main theme is already composed and others will come soon. Here is the main theme by Andrew LiVecchi: If you missed to check him out on YouTube, be sure to click the link: Youtube.com If you have feedback, you can contact me via mail or direct message whenever you want. Be sure to take a look at Twitter as well, since there will be more frequent updates. Thank you!

LukasIrzl

LukasIrzl

Warfront Infinite Dev Blog #21: The First Enemy (Animations)

This week me and my artist were working hard on the new alien model and its animations. My job was to implement the animated models into the game engine, manage transitions between different states and rewrite the enemy controller script. Since I haven't worked on skeletal animations in unity yet, it was a good learning experience and I found out that unity has this Animator component which handles transitions between animations and it is really easy to use. All I had to do was use Animator editor to create few different states (for walking, dying, getting hurt and attacking) and draw some lines between states to mark transitions. Then you can click on each of the connecting lines to edit the transition between those states and it will bring up this window: Here you can edit how fast the transitions occurs, when does it occur and more. To control the animations using the C# script all you have to do is to use GetComponent<Animator>() and then call its .Play(<animationName>) method. It will automatically do all the transitions which you created in the Animator tab. This is how the animations look in game: As you can see I added some glowing which I thought would look cool. There's still 5 more alien enemies left to do and after that we'll be working on the environment, adding buildings, new textures and overall changing the look of the levels.        

EddieK

EddieK

Frogger - Models and First Version

As I'm using Godot for this challenge there is no asset store, so I'm attempting to make all the assets myself. I'm no artist, and I find making artwork pretty tedious and time consuming .. that said I'm gradually getting more efficient at it. So I spent the first few days making artwork which is mostly in my frogger gallery. Yesterday evening and today I started actually programming the game in Godot. I'm pretty much making this up as I go from zero experience with the engine and GDScript (like when I used Unity for Tower Defence). So far I am pretty positive about the Godot experience, my only criticism so far would be the lack of in built interpolation (see this post), but it has been fairly easy to workaround. This time I'm mainly aiming at desktop rather than mobile, partly because I have no idea how good the android support is in Godot. I will cross that bridge when I come to it, but the game should in theory run fine on mobile too. The whole engine is node based, and you can have deeply nested nodes (something I'm not sure Unity supports), and handily you can make your game out of smaller scenes which you just pull in in the editor or programmatically. I like the paradigm, and the node based scene graph is similar to NDL or Ogre so I'm familiar with how to use it. My first step was to try and move the frog around the screen. For interpolation I store previous and current positions on each tick (started out at 10 ticks per second). It was pretty easy to get the frog moving left right up and down. However, from playing frogger games in a browser I realised the movement is all in 'jumps', almost like a grid (however not when on logs etc). So instead of holding a key to move the frog, each keypress gives a new destination location, and the frog makes the way to the destination, and won't respond to other moves until it has reached the destination. This is now working pretty well. For the vehicles etc I wanted a generic system, no point in programming the same thing multiple times. I have separated the lines of traffic into 'row' objects, and each row can contain multiple cars of a certain type, with certain speed and direction. There is no collision yet but I'll use just simple AABB checks. Handily, because the frog can only be on a maximum of 2 rows at a time, you only need to collide check against the cars / logs on those rows. I've also figured out how to preload the different vehicles as scenes so I can create instances which get reused as they go off the edge of the screen. I need to re-export the models as I have some newer versions now. There are also no wheels yet, and no animations, I'll deal with them later. Overall it is coming together good for just a day of programming, which is a testament to how easy Godot is to use. My first aim is to get the frog playable over the screen to get to lily pads, and die if you hit vehicles etc, and have some UI for the score and lives. I have no idea how to do menus yet in Godot but I think it will involve more scenes so will hopefully be easy. And while I'm starting with the traditional top down orthographic camera, which makes wraparound of vehicles easy, I'd like a more free moving camera, I'll just have to think of a way of making the vehicles fade in and out.

lawnjelly

lawnjelly

Unity Weekly update #15 - 【Enlightened】

Like last week, the room development is still in progress. While there are two new rooms I've also had time to tweak the lighting a bit here and there. New Lights Basically, I've tried to change the lighting of most rooms. First thing first, In order to properly shade the inside rooms I've used invisible shadow casters. At the time, it was the cheapest and quickest way to deal with it when I've started.  I did find out that another quick way to do this was with layers and light culling masks. Basically, every game objects that are inside sealed rooms is given a specific layer. That layer, in particular, was made to ignore the global directional light altogether. This means that there were no more need of these shadow casters (except on some opened rooms like the temple; the directional light can actually illuminate the room if the angle is good enough) I've also tried to fix the lighting in most rooms. Although not completely finished, it is getting prettier: The VIRTUAL Clinical Research Center As one of the newest rooms, this one represents a fictional clinical research facility named VIRTUAL Clinical Research. While in this room, the player can actually take part in a dodgy clinical trial involving either a strange cream, pills of unknown content and glowing fluids inside syringes. Each test takes away part of the player's health in exchange for cold hard cash. The ratio of health and cash is actually one to one as of yet. Taking the cream gives 5% of damage, the pills are 10% and the syringes are 25%. This room is quite useful if you're in dire need of cash, just like in real life. (except you don't get hurt as often in real life...) The Restroom This room is quite special. It a rather small room that is actually a normal public restroom, complete with a toilet, a sink, a real-time working mirror, a hand dryer, etc.  Although not functional right now, the idea is that the player can flush down one of its piece of equipment down the drain. You can only flush one piece of equipment per restroom because, well, toilets aren't really made to be able to flush down metal armours really... For those who don't know, a piece of equipment acts like a relic but is actually set to a specific equipment slot. You can only have one piece of equipment of a specific slot (for example, the player can have only one pair of gloves because it would be overpowered otherwise).  Like most RPG, different pieces of equipment have different types of stats bonuses. Each piece of equipment also has a focus alignment. This means that while the player is wearing those, its focus will progressively be drawn to whatever alignment the equipped piece is. There will also be additional stats bonuses that are applied if the player's focus matches a worn piece of equipment. But anyways, the reason the restroom isn't functional is quite simple: there's no piece of equipment yet. So it's something I'll have to get back to once there's at least one piece of equipment in the game... Next week The game is progressively coming together. Especially when it comes to rooms. There's still a lot of relics and capacities to add. There are also some rooms to add an maybe add different types of enemies and whatnot. I still want to work on rooms as of right now. The thing is that those rooms are really modelling heavy, and I really want to get those out of the way as soon as possible. The rest won't be as heavy as those, but once they're out of the way it will be a pretty big chunk of modelling that will be done. If there's time, maybe I'll work on capacities and relics...

jb-dev

jb-dev

 

This Week in Game-Guru - 10/08/2018

Official Game-Guru News: There is a new survey available which offers users the chance of getting a free DLC pack as incentive for completing it so give them your thoughts! https://www.thegamecreators.com/post/gameguru-users-enter-our-survey-to-win-a-dlc

There were also some AI improvements made to the most recent public preview. Apparently some input was run by smallg (one of the resident scripting experts) who cleaned it up.

They updated the PBR materials to include mega-pack 3:
Details on the above here: https://www.thegamecreators.com/post/gameguru-mega-pack-3-dlc-updated-2

And lastly there was ALSO an update to EAI's weaponry:https://www.thegamecreators.com/post/gameguru-mega-pack-3-dlc-updated-2

What's Good in the Store:
Tarkus's music.  I usually like his stuff but this isn't my speed.  That said it would work well for many games seeking some well priced work that can fit a wide range of modern/post modern genre games: https://www.tgcstore.net/pack/11055
Pasquill's PBR construction vehicles were completed and are now available at a very reasonable price in a pack! https://www.tgcstore.net/pack/11054

Free Stuff https://forum.game-guru.com/thread/220103 - This beautiful gateway by Lafette II
https://forum.game-guru.com/thread/220124 - This Bizarre Spongebob/Domo-kun esque character for cartoon style games.  Free, follows waypoints, has Gtox's quality on it.  Nice stuff. Characters are always a fairly expensive proposition so it's particularly noteworthy when one is available for free.


Third party tools and Tutorials There's this interesting texturing tutorial (care of Bugsy): https://www.youtube.com/watch?v=a8d6p-E4KSE

Random Acts of Creativity Amenmoses has been working on physics and particles a lot this past week.  He put together a nice demo reel of his physics-based leaves here: https://vimeo.com/292789619

There's this campfire with smoke physics and fire particles: https://vimeo.com/293305766
And he even made an aggregate thread for all of his scripting work (finally!) here: https://forum.game-guru.com/thread/220055
What a busy man!

Duchenkuke detailed his picture below (I promised not to name names but he made a video so it kind of betrays him a bit!  https://www.youtube.com/watch?v=WWsEVUgcYIw&feature=youtu.be
He's also updated his web presence, check out his new site here:
https://sites.google.com/view/dkproductions/startseite

In My Own Works:
I created a screenshot for an impromptu private contest for other GG Forumites.
Came in dead last.  Was based on 'forests'.  I gambled on going Alien Fungal and lost big!

That said, it is what it is.  Next time, I guess and no, I will not tell you where this contest was as it was fairly secret/private.  I can't post all of the pictures at this time as it will make this page unreasonably long to load for some.  So here's mine (dead last), the runner up, and the winner!


Mine:



Runner-Up:

Winner:



Congrats to the winner :)

That said I also got about 4500 words done thanks to said contest on how to make a forest, though admittedly I wrote it post-picture as a sort of 'lessons learned by my failure'.  Still, the overall nature of it came out well.  I plan on doing a city and desert/Tundra style one as well.

See you next week!


View the full article
 

OOP is dead, long live OOP

Inspiration This blog post is inspired by Aras Pranckevičius' recent publication of a talk aimed at junior programmers, designed to get them to come to terms with new "ECS" architectures. Aras follows the typical pattern (explained below), where he shows some terrible OOP code and then shows that the relational model is a great alternative solution (but calls it "ECS" instead of relational). This is not a swipe at Aras at all - I'm a fan of his work and commend him on the great presentation! The reason I'm picking on his presentation in particular instead of the hundred other ECS posts that have been made on the interwebs, is because he's gone through the effort of actually publishing a git repository to go along with his presentation, which contains a simple little "game" as a playground for demonstrating different architecture choices. This tiny project makes it easy for me to actually, concretely demonstrate my points, so, thanks Aras! You can find Aras'  slides at http://aras-p.info/texts/files/2018Academy - ECS-DoD.pdf and the code at https://github.com/aras-p/dod-playground. I'm not going to analyse the final ECS architecture from that talk (yet?), but I'm going to focus on the straw-man "bad OOP" code from the start. I'll show what it would look like if we actually fix all of the OOD rule violations.
Spoiler: fixing the OOD violations actually results in a similar performance improvement to Aras' ECS conversion, plus it actually uses less RAM and requires less lines of code than the ECS version!
TL;DR: Before you decide that OOP is shit and ECS is great, stop and learn OOD (to know how to use OOP properly) and learn relational (to know how to use ECS properly too). I've been a long-time ranter in many "ECS" threads on the forum, partly because I don't think it deserves to exist as a term (spoiler: it's just a an ad-hoc version of the relational model), but because almost every single blog, presentation, or article that promotes the "ECS" pattern follows the same structure: Show some terrible OOP code, which has a terribly flawed design based on an over-use of inheritance (and incidentally, a design that breaks many OOD rules). Show that composition is a better solution than inheritance (and don't mention that OOD actually teaches this same lesson). Show that the relational model is a great fit for games (but call it "ECS"). This structure grinds my gears because:
(A) it's a straw-man argument.. it's apples to oranges (bad code vs good code)... which just feels dishonest, even if it's unintentional and not actually required to show that your new architecture is good,
but more importantly:
(B) it has the side effect of suppressing knowledge and unintentionally encouraging readers from interacting with half a century of existing research. The relational model was first written about in the 1960's. Through the 70's and 80's this model was refined extensively. There's common beginners questions like "which class should I put this data in?", which is often answered in vague terms like "you just need to gain experience and you'll know by feel"... but in the 70's this question was extensively pondered and solved in the general case in formal terms; it's called database normalization. By ignoring existing research and presenting ECS as a completely new and novel solution, you're hiding this knowledge from new programmers. Object oriented programming dates back just as far, if not further (work in the 1950's began to explore the style)! However, it was in the 1990's that OO became a fad - hyped, viral and very quickly, the dominant programming paradigm. A slew of new OO languages exploded in popularity including Java and (the standardized version of) C++. However, because it was a hype-train, everyone needed to know this new buzzword to put on their resume, yet no one really groked it. These new languages had added a lot of OO features as keywords -- class, virtual, extends, implements -- and I would argue that it's at this point that OO split into two distinct entities with a life of their own.
I will refer to the use of these OO-inspired language features as "OOP", and the use of OO-inspired design/architecture techniques as "OOD". Everyone picked up OOP very quickly. Schools taught OO classes that were efficient at churning out new OOP programmers.... yet knowledge of OOD lagged behind. I argue that code that uses OOP language features, but does not follow OOD design rules is not OO code. Most anti-OOP rants are eviscerating code that is not actually OO code.
OOP code has a very bad reputation, I assert in part due to the fact that, most OOP code does not follow OOD rules, thus isn't actually "true" OO code. Background As mentioned above, the 1990's was the peak of the "OO fad", and it's during this time that "bad OOP" was probably at its worst. If you studied OOP during this time, you probably learned "The 4 pillars of OOP": Abstraction Encapsulation Polymorphism Inheritance I'd prefer to call these "4 tools of OOP" rather than 4 pillars. These are tools that you can use to solve problems. Simply learning how a tool works is not enough though, you need to know when you should be using them... It's irresponsible for educators to teach people a new tool without also teaching them when it's appropriate to use each of them.  In the early 2000's, there was a push-back against the rampant misuse of these tools, a kind of second-wave of OOD thought. Out of this came the SOLID mnemonic to use as a quick way to evaluate a design's strength. Note that most of these bits of advice were well actually widely circulated in the 90's, but didn't yet have the cool acronym to cement them as the five core rules... Single responsibility principle. Every class should have one reason to change. If class "A" has two responsibilities, create a new class "B" and "C" to handle each of them in isolation, and then compose "A" out of "B" and "C". Open/closed principle. Software changes over time (i.e. maintenance is important). Try to put the parts that are likely to change into implementations (i.e. concrete classes) and build interfaces around the parts that are unlikely to change (e.g. abstract base classes). Liskov substitution principle. Every implementation of an interface needs to 100% comply the requirements of that interface. i.e. any algorithm that works on the interface, should continue to work for every implementation. Interface segregation principle. Keep interfaces as small as possible, in order to ensure that each part of the code "knows about" the least amount of the code-base as possible. i.e. avoid unnecessary dependencies. This is also just good advice in C++ where compile times suck if you don't follow this advice   Dependency inversion principle. Instead of having two concrete implementations communicate directly (and depend on each other), they can usually be decoupled by formalizing their communication interface as a third class that acts as an interface between them. This could be an abstract base class that defines the method calls used between them, or even just a POD struct that defines the data passed between them. Not included in the SOLID acronym, but I would argue is just as important is the:
Composite reuse principle. Composition is the right default™. Inheritance should be reserved for use when it's absolutely required. This gives us SOLID-C(++)   A few other notes: In OOD, interfaces and implementations are ideas that don't map to any specific OOP keywords. In C++, we often create interfaces with abstract base classes and virtual functions, and then implementations inherit from those base classes... but that is just one specific way to achieve the idea of an interface. In C++, we can also use PIMPL, opaque pointers, duck typing, typedefs, etc... You can create an OOD design and then implement it in C, where there aren't any OOP language keywords! So when I'm talking about interfaces here, I'm not necessarily talking about virtual functions -- I'm talking about the idea of implementation hiding. Interfaces can be polymorphic, but most often they are not! A good use for polymorphism is rare, but interfaces are fundamental to all software. As hinted above, if you create a POD structure that simply stores some data to be passed from one class to another, then that struct is acting as an interface - it is a formal data definition. Even if you just make a single class in isolation with a public and a private section, everything in the public section is the interface and everything in the private section is the implementation. Inheritance actually has (at least) two types -- interface inheritance, and implementation inheritance. In C++, interface inheritance includes abstract-base-classes with pure-virtual functions, PIMPL, conditional typedefs. In Java, interface inheritance is expressed with the implements keyword. In C++, implementation inheritance occurs any time a base classes contains anything besides pure-virtual functions. In Java, implementation inheritance is expressed with the extends keyword. OOD has a lot to say about interface-inheritance, but implementation-inheritance should usually be treated as a bit of a code smell! And lastly I should probably give a few examples of terrible OOP education and how it results in bad code in the wild (and OOP's bad reputation). When you were learning about hierarchies / inheritance, you probably had a task something like:
Let's say you have a university app that contains a directory of Students and Staff. We can make a Person base class, and then a Student class and a Staff class that inherit from Person!
Nope, nope nope. Let me stop you there. The unspoken sub-text beneath the LSP is that class-hierarchies and the algorithms that operate on them are symbiotic. They're two halves of a whole program. OOP is an extension of procedural programming, and it's still mainly about those procedures. If we don't know what kinds of algorithms are going to be operating on Students and Staff (and which algorithms would be simplified by polymorphism) then it's downright irresponsible to dive in and start designing class hierarchies. You have to know the algorithms and the data first. When you were learning about hierarchies / inheritance, you probably had a task something like:
Let's say you have a shape class. We could also have squares and rectangles as sub-classes. Should we have square is-a rectangle, or rectangle is-a square?
This is actually a good one to demonstrate the difference between implementation-inheritance and interface-inheritance. If you're using the implementation-inheritance mindset, then the LSP isn't on your mind at all and you're only thinking practically about trying to reuse code using inheritance as a tool.
From this perspective, the following makes perfect sense:
struct Square { int width; }; struct Rectangle : Square { int height; };
A square just has width, while rectangle has a width + height, so extending the square with a height member gives us a rectangle! As you might have guessed, OOD says that doing this is (probably) wrong. I say probably because you can argue over the implied specifications of the interface here... but whatever.
A square always has the same height as its width, so from the square's interface, it's completely valid to assume that its area is "width * width".
By inheriting from square, the rectangle class (according to the LSP) must obey the rules of square's interface. Any algorithm that works correctly with a square, must also work correctly with a rectangle. Take the following algorithm: std::vector<Square*> shapes; int area = 0; for(auto s : shapes) area += s->width * s->width;
This will work correctly for squares (producing the sum of their areas), but will not work for rectangles.
Therefore, Rectangle violates the LSP rule. If you're using the interface-inheritance mindset, then neither Square or Rectangle will inherit from each other. The interface for a square and rectangle are actually different, and one is not a super-set of the other. So OOD actually discourages the use of implementation-inheritance. As mentioned before, if you want to re-use code, OOD says that composition is the right way to go! For what it's worth though, the correct version of the above (bad) implementation-inheritance hierarchy code in C++ is:
struct Shape { virtual int area() const = 0; };
struct Square : public virtual Shape { virtual int area() const { return width * width; }; int width; };
struct Rectangle : private Square, public virtual Shape { virtual int area() const { return width * height; }; int height; }; "public virtual" means "implements" in Java. For use when implementing an interface. "private" allows you to extend a base class without also inheriting its interface -- in this case, Rectangle is-not-a Square, even though it's inherited from it. I don't recommend writing this kind of code, but if you do like to use implementation-inheritance, this is the way that you're supposed to be doing it! TL;DR - your OOP class told you what inheritance was. Your missing OOD class should have told you not to use it 99% of the time! Entity / Component frameworks With all that background out of the way, let's jump into Aras' starting point -- the so called "typical OOP" starting point.
Actually, one last gripe -- Aras calls this code "traditional OOP", which I object to. This code may be typical of OOP in the wild, but as above, it breaks all sorts of core OO rules, so it should not all all be considered traditional. I'm going to start from the earliest commit before he starts fixing the design towards "ECS": "Make it work on Windows again" 3529f232510c95f53112bbfff87df6bbc6aa1fae // ------------------------------------------------------------------------------------------------- // super simple "component system" class GameObject; class Component; typedef std::vector<Component*> ComponentVector; typedef std::vector<GameObject*> GameObjectVector; // Component base class. Knows about the parent game object, and has some virtual methods. class Component { public: Component() : m_GameObject(nullptr) {} virtual ~Component() {} virtual void Start() {} virtual void Update(double time, float deltaTime) {} const GameObject& GetGameObject() const { return *m_GameObject; } GameObject& GetGameObject() { return *m_GameObject; } void SetGameObject(GameObject& go) { m_GameObject = &go; } bool HasGameObject() const { return m_GameObject != nullptr; } private: GameObject* m_GameObject; }; // Game object class. Has an array of components. class GameObject { public: GameObject(const std::string&& name) : m_Name(name) { } ~GameObject() { // game object owns the components; destroy them when deleting the game object for (auto c : m_Components) delete c; } // get a component of type T, or null if it does not exist on this game object template<typename T> T* GetComponent() { for (auto i : m_Components) { T* c = dynamic_cast<T*>(i); if (c != nullptr) return c; } return nullptr; } // add a new component to this game object void AddComponent(Component* c) { assert(!c->HasGameObject()); c->SetGameObject(*this); m_Components.emplace_back(c); } void Start() { for (auto c : m_Components) c->Start(); } void Update(double time, float deltaTime) { for (auto c : m_Components) c->Update(time, deltaTime); } private: std::string m_Name; ComponentVector m_Components; }; // The "scene": array of game objects. static GameObjectVector s_Objects; // Finds all components of given type in the whole scene template<typename T> static ComponentVector FindAllComponentsOfType() { ComponentVector res; for (auto go : s_Objects) { T* c = go->GetComponent<T>(); if (c != nullptr) res.emplace_back(c); } return res; } // Find one component of given type in the scene (returns first found one) template<typename T> static T* FindOfType() { for (auto go : s_Objects) { T* c = go->GetComponent<T>(); if (c != nullptr) return c; } return nullptr; } Ok, 100 lines of code is a lot to dump at once, so let's work through what this is... Another bit of background is required -- it was popular for games in the 90's to use inheritance to solve all their code re-use problems. You'd have an Entity, extended by Character, extended by Player and Monster, etc... This is implementation-inheritance, as described earlier (a code smell), and it seems like a good idea to begin with, but eventually results in a very inflexible code-base. Hence that OOD has the "composition over inheritance" rule, above. So, in the 2000's the "composition over inheritance" rule became popular, and gamedevs started writing this kind of code instead. What does this code do? Well, nothing good   To put it in simple terms, this code is re-implementing the existing language feature of composition as a runtime library instead of a language feature. You can think of it as if this code is actually constructing a new meta-language on top of C++, and a VM to run that meta-language on. In Aras' demo game, this code is not required (we'll soon delete all of it!) and only serves to reduce the game's performance by about 10x. What does it actually do though? This is an "Entity/Component" framework (sometimes confusingly called an "Entity/Component system") -- but completely different to an "Entity Component System" framework (which are never called "Entity Component System systems" for obvious reasons). It formalizes several "EC" rules: the game will be built out of featureless "Entities" (called GameObjects in this example), which themselves are composed out of "Components". GameObjects fulfill the service locator pattern -  they can be queried for a child component by type.  Components know which GameObject they belong to - they can locate sibling componets by querying their parent GameObject. Composition may only be one level deep (Components may not own child components, GameObjects may not own child GameObjects). A GameObject may only have one component of each type (some frameworks enforced this, others did not). Every component (probably) changes over time in some unspecified way - so the interface includes "virtual void Update". GameObjects belong to a scene, which can perform queries over all GameObjects (and thus also over all Components). This kind of framework was very popular in the 2000's, and though restrictive, proved flexible enough to power countless numbers of games from that time and still today. However, it's not required. Your programming language already contains support for composition as a language feature - you don't need a bloated framework to access it... Why do these frameworks exist then? Well to be fair, they enable dynamic, runtime composition. Instead of GameObject types being hard-coded, they can be loaded from data files. This is great to allow game/level designers to create their own kinds of objects... However, in most game projects, you have a very small number of designers on a project and a literal army of programmers, so I would argue it's not a key feature. Worse than that though, it's not even the only way that you could implement runtime composition! For example, Unity is based on C# as a "scripting language", and many other games use alternatives such as Lua -- your designer-friendly tool can generate C#/Lua code to define new game-objects, without the need for this kind of bloated framework! Let's evaluate this code according to OOD: GameObject::GetComponent uses dynamic_cast. Most people will tell you that dynamic_cast is a code smell - a strong hint that something is wrong. I would say that it indicates that you have an LSP violation on your hands -- you have some algorithm that's operating on the base interface, but it demands to know about different implementation details. That's the specific reason that it smells. GameObject is kind of ok if you imagine that it's fulfilling the service locator pattern.... but going beyond OOD critique for a moment, this pattern creates implicit links between parts of the project, and I feel (without a wikipedia link to back me up with comp-sci knowledge) that implicit communication channels are an anti-pattern and explicit communication channels should be preferred. This same argument applies to bloated "event frameworks" that sometimes appear in games... I would argue that Component is a SRP violation because its interface (virtual void Update(time)) is too broad. The use of "virtual void Update" is pervasive within game development, but I'd also say that it is an anti-pattern. Good software should allow you to easily reason about the flow of control, and the flow of data. Putting every single bit of gameplay code behind a "virtual void Update" call completely and utterly obfuscates both the flow of control and the flow of data. IMHO, invisible side effects, a.k.a. action at a distance, is the most common source of bugs, and "virtual void Update" ensures that almost everything is an invisible side-effect. Even though the goal of the Component class is to enable composition, it's doing so via inheritance, which is a CRP violation. The one good part is that the example game code is bending over backwards to fulfill the SRP and ISP rules -- it's split into a large number of simple components with very small responsibilities, which is great for code re-use.
However, it's not great as DIP -- many of the components do have direct knowledge of each other. So, all of the code that I've posted above, can actually just be deleted. That whole framework. Delete GameObject (aka Entity in other frameworks), delete Component, delete FindOfType. It's all part of a useless VM that's breaking OOD rules and making our game terribly slow. Frameworkless composition (AKA using the features of the #*@!ing programming language) If we delete our composition framework, and don't have a Component base class, how will our GameObjects manage to use composition and be built out of Components. As hinted in the heading, instead of writing that bloated VM and then writing our GameObjects on top of it in our weird meta-language, let's just write them in C++ because we're #*@!ing game programmers and that's literally our job. Here's the commit where the Entity/Component framework is deleted: https://github.com/hodgman/dod-playground/commit/f42290d0217d700dea2ed002f2f3b1dc45e8c27c
Here's the original version of the source code: https://github.com/hodgman/dod-playground/blob/3529f232510c95f53112bbfff87df6bbc6aa1fae/source/game.cpp
Here's the modified version of the source code: https://github.com/hodgman/dod-playground/blob/f42290d0217d700dea2ed002f2f3b1dc45e8c27c/source/game.cpp The gist of the changes is: Removing ": public Component" from each component type. I add a constructor to each component type. OOD is about encapsulating the state of a class, but since these classes are so small/simple, there's not much to hide -- the interface is a data description. However, one of the main reasons that encapsulation is a core pillar is that it allows us to ensure that class invariants are always true... or in the event that an invariant is violated, you hopefully only need to inspect the encapsulated implementation code in order to find your bug. In this example code, it's worth us adding the constructors to enforce a simple invariant -- all values must be initialized. I rename the overly generic "Update" methods to reflect what they actually do -- UpdatePosition for MoveComponent and ResolveCollisions for AvoidComponent. I remove the three hard-coded blocks of code that resemble a template/prefab -- code that creates a GameObject containing specific Component types, and replace it with three C++ classes. Fix the "virtual void Update" anti-pattern. Instead of components finding each other via the service locator pattern, the game objects explicitly link them together during construction. The objects So, instead of this "VM" code: // create regular objects that move for (auto i = 0; i < kObjectCount; ++i) { GameObject* go = new GameObject("object"); // position it within world bounds PositionComponent* pos = new PositionComponent(); pos->x = RandomFloat(bounds->xMin, bounds->xMax); pos->y = RandomFloat(bounds->yMin, bounds->yMax); go->AddComponent(pos); // setup a sprite for it (random sprite index from first 5), and initial white color SpriteComponent* sprite = new SpriteComponent(); sprite->colorR = 1.0f; sprite->colorG = 1.0f; sprite->colorB = 1.0f; sprite->spriteIndex = rand() % 5; sprite->scale = 1.0f; go->AddComponent(sprite); // make it move MoveComponent* move = new MoveComponent(0.5f, 0.7f); go->AddComponent(move); // make it avoid the bubble things AvoidComponent* avoid = new AvoidComponent(); go->AddComponent(avoid); s_Objects.emplace_back(go); } We now have this normal C++ code: struct RegularObject { PositionComponent pos; SpriteComponent sprite; MoveComponent move; AvoidComponent avoid; RegularObject(const WorldBoundsComponent& bounds) : move(0.5f, 0.7f) // position it within world bounds , pos(RandomFloat(bounds.xMin, bounds.xMax), RandomFloat(bounds.yMin, bounds.yMax)) // setup a sprite for it (random sprite index from first 5), and initial white color , sprite(1.0f, 1.0f, 1.0f, rand() % 5, 1.0f) { } }; ... // create regular objects that move regularObject.reserve(kObjectCount); for (auto i = 0; i < kObjectCount; ++i) regularObject.emplace_back(bounds); The algorithms Now the other big change is in the algorithms. Remember at the start when I said that interfaces and algorithms were symbiotic, and both should impact the design of the other? Well, the "virtual void Update" anti-pattern is also an enemy here. The original code has a main loop algorithm that consists of just: // go through all objects for (auto go : s_Objects) { // Update all their components go->Update(time, deltaTime); You might argue that this is nice and simple, but IMHO it's so, so bad. It's completely obfuscating both the flow of control and the flow of data within the game. If we want to be able to understand our software, if we want to be able to maintain it, if we want to be able to bring on new staff, if we want to be able to optimise it, or if we want to be able to make it run efficiently on multiple CPU cores, we need to be able to understand both the flow of control and the flow of data. So "virtual void Update" can die in a fire. Instead, we end up with a more explicit main loop that makes the flow of control much more easy to reason about (the flow of data is still obfuscated here, we'll get around to fixing that in later commits) // Update all positions for (auto& go : s_game->regularObject) { UpdatePosition(deltaTime, go, s_game->bounds.wb); } for (auto& go : s_game->avoidThis) { UpdatePosition(deltaTime, go, s_game->bounds.wb); } // Resolve all collisions for (auto& go : s_game->regularObject) { ResolveCollisions(deltaTime, go, s_game->avoidThis); } The downside of this style is that for every single new object type that we add to the game, we have to add a few lines to our main loop. I'll address / solve this in a future blog in this series. Performance There's still a lot of outstanding OOD violations, some bad design choices, and lots of optimization opportunities remaining, but I'll get to them with the next blog in this series. As it stands at this point though, the "fixed OOD" version either almost matches or beats the final "ECS" code from the end of the presentation... And all we did was take the bad faux-OOP code and make it actually obey the rules of OOP (and delete 100 lines of code)! Next steps There's much more ground that I'd like to cover here, including solving the remaining OOD issues, immutable objects (functional style programming) and the benefits it can bring to reasoning about data flows, message passing, applying some DOD reasoning to our OOD code, applying some relational wisdom to our OOD code, deleting those "entity" classes that we ended up with and having purely components-only, different styles of linking components together (pointers vs handles), real world component containers, catching up to the ECS version with more optimization, and then further optimization that wasn't also present in Aras' talk (such as threading / SIMD). No promises on the order that I'll get to these, or if, or when...  

Hodgman

Hodgman

 

Graphics Programming weekly - Issue 57 — October 7, 2018

niagara: Cleaning up Vulkan stream - part 3 fixing validation layer errors, explanation of pipeline barriers implementation of swap chain resize shader compiler integration with Visual Studio niagara: Rendering a mesh Vulkan stream - part 4 overview and integration of Vulkan extension loader loading an .obj and rendering a mesh using classical vertex pipeline and manual vertex pulling “Cheap” Diamond Rendering explanation of shader for diamond rendering in a forward rendering architecture uses a precalculated cubemap from the inside of the object to simulate internal bounces wayback-archive My journey into fractals discussing many aspects of making a voxel-based game including cone tracing, lighting, shadows, ambient occlusion, volumetric lighting, and atmospheric effects wayback-archive Another View on the Classic Ray-AABB Intersection Algorithm for BVH Traversal presents the steps required to vectorize the Ray-AABB approach by Andrew Kensler results are very similar to the recent jcgt paper wayback-archive Running Average Encoding - Why It Works derivation of the progressive spherical Gaussian encoding technique discussed in last week’s issue wayback-archive Porting My CUDA Path Tracer to ptx-builder/linker explores changes to CUDA path tracer with Rust, what improved since the last post and what problems persists wayback-archive GLSL cross-compiler tool command line tool that allows cross compilation from GLSL to HLSL, GLES and MSL (Metal) implemented using a combination of Glslang and SPIRV-cross A Comparison of Modern Graphics APIs work in progress post comparing API concepts between D3D12, Vulkan, Metal, and OpenGL wayback-archive DirectX Raytracing and the Windows 10 October 2018 Update Windows raytracing support is available starting with Windows 10 October 2018 update wayback-archive Video Series: Real-Time Ray Tracing for Interactive Global Illumination Workflows in Frostbite breakdown of “Real-Time Ray Tracing for Interactive Global Illumination Workflows in Frostbite” into short sections key takeaways for each section is provided in the post wayback-archive Parsing Shader Includes explains how to parse a shader file for includes using regex (implemented using Rust) wayback-archive SRP101 collection of resources to get started with the Unity Scriptable Render Pipeline wayback-archive Perspective Matrices and Depth visualization of floating point precision when a classical OpenGL projection matrix is used wayback-archive Art That Moves: Creating Animated Materials with Shader Graph explanation of Unity shader graph nodes walkthrough of example shaders that allow the creation of a small island scene wayback-archive PIX-1810.02 – Direct3D11 and WinML new PIX features are available with Windows 10 October 2018 can capture D3D11 applications using Direct3D 11 on 12 translation layer and WinML workloads wayback-archive Vulkan SDK 1.1.85.0 adds support for Turing extensions wayback-archive Qualcomm Releases Snapdragon Profiler with Vulkan Support new profiler version now supports Vulkan wayback-archive If you are enjoying the series and getting value from it, please consider supporting this blog. Support this blog
Read more

jendrikillner

jendrikillner

Quest a day challenge

The first week of October is over. I was writing quests and NPCs. The main goal was to write at least one quest a day. And this is really interesting and fun!  My total result for this week is 15 quests and 20 NPCs. There is a lot of work on polishing ‘em all but at least I captured the main idea on every quest and NPC. Today I skipped and did nothing but analyze the whole result. Tried to make a document where I can store quest data (conditions, dialogues, rewards, stages and thoughts). Now I’m planning on prototyping quests via Creation Kit (Skyrim). But still there are relatively negative moments. I’m not satisfied because there is no real motivation for a player to complete those quests (as I think) and quests a simple as hell. They are more MMO like actually. Yes, they are not about killing n amount of slimes but still they don’t force player to use game features. Like why should anyone get fun from a quest that is fully independent from the game? Or maybe good narrative is enough for an engaging & fun adventure? This made me think about the main game mechanics. Those that player will use real often regardless of the playstyle. Of course I should start from movement as my game features an extended movement system which includes crawling and climbing mechanics.  Example: Assassins Creed where player MUST use those mechs, so he can climb up to a viewpoint and open quests markings on a map and he MAY use them to avoid enemy NPCs in a different type of situations. This mechanics was used in solving puzzles and getting to a right place quests.  Well, will see how all of this thoughts, quests and NPCs will evolve a week after 😛

k0fe

k0fe

Procedural Landscape or how to Shatter a Planet! (with actual code examples)

I've spent the last few days coding in my Economy Server, I've almost completed the functionality of Banking!  So, it's truly time for a rewind about something entirely different to clear my head. Here's an editor view from a little while back, before I was loading a "visible planet" object, the procedurally loaded landscape was the only visible landscape, the rest of the planet, if you could see it before the landscape loaded, just looked like water. So, let me start off by clarifying, the landscape in my game is generated prodedurally, however it is not "generated" during game play. I have a separate process that I can run on any sculptured sphere of my choosing that will generate the individual triangular mesh objects and then save them into "tile" files.  These tile files also contain place holders for items such as Arrays of Trees, Rocks, and Plants. Let's start at the Crawler!  The following script is the code that crawls from triangle to triangle along the planet mesh and kicks off the generation of the tiles. using System.Collections; using System.Collections.Generic; using UnityEngine; public class MeshCrawler : MonoBehaviour { public Vector3[] verts; public int[] tris; public Vector3[] norms; public int curTri = 0; public int startTri = -1; public int endTri; public string meshName; public List<int> triBuffer = new List<int>(); bool crawling = false; // Use this for initialization void Start () { verts = GetComponent<MeshFilter>().mesh.vertices; norms = GetComponent<MeshFilter>().mesh.normals; tris = GetComponent<MeshFilter>().mesh.triangles; meshName = GetComponent<MeshFilter>().mesh.name; } IEnumerator CrawlIT() { crawling = true; if (endTri > (tris.Length / 3)) endTri = tris.Length / 3; if (startTri > -1 && endTri > 0) { Debug.Log("Crawling " + startTri.ToString() + " to " + endTri.ToString()); for (int i = startTri; i < endTri; i++) { curTri = i; Vector3 p0 = verts[tris[i * 3 + 0]]; Vector3 p1 = verts[tris[i * 3 + 1]]; Vector3 p2 = verts[tris[i * 3 + 2]]; Vector3 n0 = norms[tris[i * 3 + 0]]; Vector3 n1 = norms[tris[i * 3 + 1]]; Vector3 n2 = norms[tris[i * 3 + 2]]; UWTerrainTri triCandidate = new UWTerrainTri(); triCandidate.triIndex = i; triCandidate.meshName = "PlanetZed"; triCandidate.points = new Vector3[3] { p0, p1, p2 }; triCandidate.normals = new Vector3[3] { n0, n1, n2 }; TerrainController.TriHit(triCandidate); //Debug.Log("Tri: " + i.ToString()); yield return null; } } Debug.Log("Crawler finished."); crawling = false; } // Update is called once per frame void Update () { if (triBuffer.Count > 0) { if (!crawling) { startTri = triBuffer[0] - 5; endTri = triBuffer[0] + 5; run = true; triBuffer.RemoveAt(0); } } if (!crawling) { StartCoroutine(CrawlIT()); } } } Okay, so what's it doing? This script is attached to the planet mesh object and it starts a Coroutine(Unity safe psudo-thread a "function that yields") that handles all the data gathering steps. This routine grabs the triangles array from the MeshFilter and works its way from 0 to the end, pulling the vertices out of the MeshFilters verts array, stuffing those into a class that is passed to another bit of code.  The following code decides how to handle the UWTerrainTri class objects that are passed from the Crawler: //The above code sends it's result to a "TriHit()" routine. //that routine has terrain Loading functionality in it, in this mode mode //it just passes the UWTerrainTri object to GenTriLand(): public List<Vector3> vertsWTrees = new List<Vector3>(); public void GenTriLand(UWTerrainTri uWTerrainTri) { //Create a new mesh object. Mesh mesh = new Mesh(); //This is the class that is saved as the "tile" object. TerrainSegment ts = new TerrainSegment(); //Generate a good file name. string tpName = uWTerrainTri.meshName + uWTerrainTri.triIndex.ToString("0000000"); //Grab the vertices. ts.vertices = uWTerrainTri.points; //500,000 files in the same directory is a BAD idea. ;) string dir1 = tpName.Substring(9, 2); string dir2 = tpName.Substring(11, 1); dir2 = "0" + dir2; //Generate a full file name. string fullFilePath = Application.persistentDataPath + "/tdat/" + dir1 + "/" + dir2 + "/" + tpName + ".dat"; mesh.vertices = ts.vertices; mesh.uv = new Vector2[3] { new Vector2(0, 0), new Vector2(0, 1), new Vector2(1, 1) }; mesh.triangles = new int[] { 0, 1, 2 }; mesh.RecalculateBounds(); mesh.RecalculateNormals(); ts.vertices = mesh.vertices; ts.normals = mesh.normals; ts.uvs = mesh.uv; ts.Tris = mesh.triangles; //Data Objects for Rocks, Grass, Trees List<EnvironmentObject> rl = new List<EnvironmentObject>(); List<EnvironmentObject> gl = new List<EnvironmentObject>(); List<EnvironmentObject> tl = new List<EnvironmentObject>(); List<Vector3> usedVertices = new List<Vector3>(); //Rocks int r = UnityEngine.Random.Range(3, 12); for (int i = 0; i < r; i++) { Vector3 point = GetRandomPointOnMesh(mesh); int tryCount = 0; while (usedVertices.Contains(point)) { tryCount++; point = GetRandomPointOnMesh(mesh); if (tryCount > 10) goto NoRock;//Yeah goto just works sometimes, hate me if you want. } usedVertices.Add(point); EnvironmentObject eo = new EnvironmentObject(); eo.prefab = "rock" + UnityEngine.Random.Range(1, 3).ToString() + "sm"; eo.pos = point; eo.rot = Quaternion.identity; eo.scale = Vector3.one; rl.Add(eo); NoRock:; } usedVertices.Clear(); //Grass int g = UnityEngine.Random.Range(2, 8); for (int i = 0; i < g; i++) { Vector3 point = GetRandomPointOnMesh(mesh); int tryCount = 0; while (usedVertices.Contains(point)) { tryCount++; point = GetRandomPointOnMesh(mesh); if (tryCount > 10) goto NoGrass; } usedVertices.Add(point); EnvironmentObject eo = new EnvironmentObject(); eo.prefab = "grass" + UnityEngine.Random.Range(1, 4).ToString(); eo.pos = point; // + new Vector3(UnityEngine.Random.Range(-5.0f, 5.0f), 0, UnityEngine.Random.Range(-5.0f, 5.0f)); eo.rot = Quaternion.identity; eo.scale = Vector3.one; gl.Add(eo); NoGrass:; } usedVertices.Clear(); //Trees int t = UnityEngine.Random.Range(0, 3); for (int i = 0; i < t; i++) { Vector3 point = mesh.vertices[UnityEngine.Random.Range(0, mesh.vertices.Length)]; int tryCount = 0; while (vertsWTrees.Contains(point)) { tryCount++; point = mesh.vertices[UnityEngine.Random.Range(0, mesh.vertices.Length)]; if (tryCount > 10) goto NoTree; } vertsWTrees.Add(point); EnvironmentObject eo = new EnvironmentObject(); eo.prefab = "tree" + UnityEngine.Random.Range(1, 3).ToString(); eo.pos = point; GameObject tree = new GameObject(); tree.transform.SetParent(planet.transform); tree.transform.localPosition = eo.pos; float maxs = 10; if (eo.prefab == "tree2") maxs = 15; FixTree(tree.transform, 5, maxs, 10); eo.pos = tree.transform.localPosition; eo.rot = tree.transform.localRotation; eo.scale = tree.transform.localScale; Destroy(tree); tl.Add(eo); NoTree:; } //Housekeeping for performance. if (vertsWTrees.Count > 10000) { while (vertsWTrees.Count > 10000) { vertsWTrees.RemoveAt(0); } } ts.rocks = rl.ToArray(); ts.grass = gl.ToArray(); ts.trees = tl.ToArray(); //Save terrain "tile" file. string json = JsonUtility.ToJson(ts); File.WriteAllText(fullFilePath, json); //Debug.Log("New Terrain Generated: " + tpName); } [Serializable] public class TerrainSegment { public Vector3[] vertices; public Vector3[] normals; public Vector2[] uvs; public int[] Tris; public EnvironmentObject[] rocks; public EnvironmentObject[] grass; public EnvironmentObject[] trees; } [Serializable] public class EnvironmentObject { public string prefab; public Vector3 pos; public Quaternion rot; public Vector3 scale; } This code produces a file for each triangle in the planet mesh.  These files are then "streamed" to the client when any of many "mesh agents"(attached to the player object) in the game does a raycast at the ground and discovers a triangle ID that has not been loaded.  The game client will then send a request to the server to download the tile number that hasn't been found in the local cache of tile files. Which as you can see from the above image, it works pretty swimmingly. Let me know what ya think!  

Septopus

Septopus

 

Fourth Entry - October 7, 2018

Greeting readers! This is the fourth entry of my development blog for my project 'Tracked Mind'. This month I've been focusing on learning how animations work in Unreal Engine 4, especially how different character stances would look and how different animations blend together, and to make it all look good of Course. I am very pleased with the result and that I will be able to use what I have learned in this project. I have also been doing some research on the lore of the game and how non-human creatures would fit into the story.  The idea of adding an ability to do a short backwards dodge does not seem to be a something that fits the game’s pace, it might make the game too easy in certain situations as well, but I will keep testing it to see if I change my mind. Sadly no new screenshots or development videos yet but I’m planning on uploading some during october so stay put. Some changes this month:
General changes Added new effects for some enemies that will "evaporate" when killed.
Added new weather effects Thank you for reading! /Mikael Henriksson

Frogger GameDev Challenge - Part 1 - Frog GFX

Hello everyone! The next challenge has been announced for Fall with the goal to create a game with similar elements as Frogger.   This is going to be an interesting challenge because I'm using this opportunity to try out the Unity Engine. For those that know me here, I've mainly use libraries and code my games from that, otherwise I would use Unreal if I wanted to make a serious 3D game. I normally use C++ in engine programming, tool-kits and my games, but I did use C# a lot back in the XNA days, but I don't have any doubt in my ability to code using C# for this project. My goal is to create the basic level where the frog will need to avoid traffic, and the water hazards before landing on the lily pad. I'll be creating all the visual assets myself, but will find sound and/or music from a royalty free site as I'm primarily a programmer and artist. Below you'll get a look at the Frog character I made. He is fully textured, and game ready. I've also rigged the mesh and I'm working on the animations. I need to fix up the eye control as I did the lazy approach when connecting them. This is the first "frog" I've ever made, but I'm happy with it.   Low Poly: Rendered: Low Poly after being Baked from High Poly. Then I texture painted the maps. Rig Test:   I'll be setting up goals I need to achieve as I work through this, but my main goals are finishing the graphical assets first and completing the animations so I can spend more time toying with Unity. My current asset list right now is (This may change later): - Wood Log - Road - Water - Ground (mix of grass and dirt) - Lily Pad - Truck - Car - Boat (or a small Sea-Doo) Other Important To-Dos - Complete jumping animation for the frog - Make a small mock-up for the menus and basic interface    Hopefully my next update will have a mock-up.  Thanks for reading. If you're interested in joining in check out the challenge post below:    

Rutin

Rutin

  • Advertisement
  • Advertisement
  • Popular Blogs

  • Advertisement
  • Blog Comments

    • Sure, I'll try. It can be a bit hard to explain though........ We have an octree of voxels divided into chunks. A chunk is basically some point in the octree and everything below it. The depth of leaf nodes for a chunk is determined by the distance the chunk is from the player. However we don't build down to our leaf nodes unless there is a sign change, (i.e. mesh data) otherwise it would defeat the purpose of the octree.  So most parts of a chunk's octree or even the whole octree will not go all the way down to it's leaf depth, and it's leaf depth could be several levels down from a bottom voxel.......The problem is as you approach an area of terrain, the leaf node level will increase to provide more detail, and we can not assume that just because a voxel has no data it's children will not have data also.  A good example of this is if you have some floating terrain in the middle of a voxel.  However this can also happen without floating terrain.  For instance if some geometry pierces the side of a voxel but doesn't contact any of it's corners the voxel will appear empty, but after you subdivide it you might find data. So for build iterations we have to go all the way down to where leaf nodes should be, just to check if there is data.  We need to do this because our terrain functions accept X,Y,Z coordinates and we don't have those until we have built the octree down. So it's kind of a chicken and egg problem. We need the octree to find the data, and we need the data to figure out where the octree should be.  Instead of building the whole octree down however, we use a lightweight tree without all the faces edges and nodes, just to find coordinates to feed to our functions.  Since our coordinates are on the surface of a sphere we need to subdivide an icosahedron and that's really what our unit sphere is. Then to calculate the position of a given voxel vertex we just multiply it's corresponding unit sphere vertex by it's altitude to get our final voxel vertex X,Y,Z coordinates. We are really dong this for virtual vertexes since they don't exist yet. The ghost-tree code handles this stuff.  Also we don't un-subdivide the unit sphere on every iteration. We use it to cache the sphere related part of our coordinates between build iterations, in addition to height related components of any terrain functions we are using. We therefore don't need to recalculate all that stuff on each iteration. We just recalculate the height part when we do our ghost walking which is a simple calculation..... Our ghost walking is implemented by recursion and on the way back up we throw away any parts of the ghost tree with no data, so we are left with a map of how to build the actual tree. Note this whole processes is only done on cells that appear empty.  Cells that already have a sign change will be at the current leaf level, and when we increase the level we can just subdivide them normally.   This handles the vast majority of cases. However as I said you can't make the assumption because for anything but the simplest cases it won't hold true very long.  Generally you won't have to ghost walk very deep but there are cases where it's possible. For instance say you have a long thin spike that avoids all nodes up until the last level of LOD. There is one more feature I didn't really talk about yet but I'll add it in here.  if we have simple height-map data, we can store those terrain function values in the unit-sphere vertexes no problem.  However when we start getting into caves and stuff like that, you really can't do that.  So now there is another problem. As as you build your ghost tree down to find your data, you are running terrain functions on 3D coordinates and those can be expensive. Note that each voxel vertex is used by 12 voxels ( just like for cube voxels, each for vertex is used by 8 voxels).  Since during the ghost walk process there are no voxel vertexes built yet, there is no place to store calculated terrain function values.  Ostensibly you would have to calculate the same terrain function value 12 times, but that would be a disaster. Instead we have a 3D matrix cache that we use for temporarily storing terrain function values as we search for data,  and there is a special "chunk address" that we index it with so we can quickly see if we have already calculated a needed value. Since our voxels are prisms it uses something akin to barycentric integer coordinates. In fact there is a partially implicit addressing system that assigns each voxel a unique ID.  I call it GNOLLS coordinators If you you start at the top, the world is constructed from an icosahedron. That's twenty faces. we can combined those into pairs to a get a diamond shape which we call a "Group".  The group has two axes one that points roughly "North" and the other is at an "Oblique" angle to it.   That gets us to a pair of voxels which we call "Legs" (a term I stole form investment jargon, there is a one "up" Leg and one point "down" Leg). Then we have the altitude of the a voxel which we call a "Level" and finally we have the "Subdivision" as defined by our octree. So its:

      Group
      North
      Oblique
      Leg
      Level
      Subdivision GNOLLS! (Yes I had to think a while to make the acronym work )   
    • does the lava slosh around?
    • Wow, that all looks very awesome.  Please keep the pictures coming.
    • @Gnollrunner, can you explain this part "We also use our unit sphere to help the horizontal part of our voxel subdivision operation. By referencing the unit sphere we only have to multiply a unit sphere vertex by a height value to generate voxel vertex coordinates.  Finally our unit-sphere is also used to provide coordinates during the ghost-walking process we talked about in our first entry.  Without it, our ghost-walking would be more computationally expensive as it would have to calculate spherical coordinates on each iteration instead of just calculating heights, which are quite simple to calculate as they are all generated by simply averaging two other heights."  A little more?
    • Good stuff dood! How (roughly) are you doing your lava, or is it top secret lol? Look forward to your frogger post!
  • Advertisement
  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!