Tuesday, March 19, 2019

AAGD

tag:blogger.com,1999:blog-3223852017784708240.archive2015-01-24T20:21:37.696-05:00Adventures of An Amateur GameDevMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comBloggertag:blogger.com,1999:blog-3223852017784708240.layout2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Template: Adventures of An Amateur GameDev<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE HTML> <html b:render='false' b:version='2' class='v2' expr:dir='data:blog.languageDirection' xmlns='http://www.w3.org/1999/xhtml' xmlns:b='http://www.google.com/2005/gml/b' xmlns:data='http://www.google.com/2005/gml/data' xmlns:expr='http://www.google.com/2005/gml/expr'> <head> <b:include data='blog' name='all-head-content'/> <title><data:blog.pageTitle/></title> <meta content='!' name='fragment'/> <meta content='dynamic' name='blogger-template'/> <meta content='IE=9,chrome=1' http-equiv='X-UA-Compatible'/> <meta content='initial-scale=1.0, maximum-scale=1.0, user-scalable=no, width=device-width' name='viewport'/> <b:skin><![CDATA[/*----------------------------------------------- Blogger Template Style Name: Dynamic Views ----------------------------------------------- */ /* Variable definitions ==================== <Variable name="keycolor" description="Main Color" type="color" default="#ffffff" variants="#2b256f,#00b2b4,#4ba976,#696f00,#b38f00,#f07300,#d0422c,#f37a86,#7b5341" value="#e9ba52"/> <Group description="Page"> <Variable name="page.text.font" description="Font" type="font" default="'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, Arial, sans-serif" value="'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, Arial, sans-serif"/> <Variable name="page.text.color" description="Text Color" type="color" default="#333333" variants="#333333" value="#000000"/> <Variable name="body.background.color" description="Background Color" type="color" default="#EEEEEE" variants="#dfdfea,#d9f4f4,#e4f2eb,#e9ead9,#f4eed9,#fdead9,#f8e3e0,#fdebed,#ebe5e3" value="#e9e9e9"/> </Group> <Variable name="body.background" description="Body Background" type="background" color="#EEEEEE" default="$(color) none repeat scroll top left" value="#e9ba52 url(//themes.googleusercontent.com/image?id=0BwVBOzw_-hbMN2IwNWVhMDktYzNlYy00MGY2LWI5MTYtMTQ2ZTRkMmI3YTE1) repeat fixed top center /* Credit: mammuth (http://www.istockphoto.com/googleimages.php?id=10364914&platform=blogger) */"/> <Group description="Header"> <Variable name="header.background.color" description="Background Color" type="color" default="#F3F3F3" variants="#F3F3F3" value="#e9e9e9"/> </Group> <Group description="Header Bar"> <Variable name="primary.color" description="Background Color" type="color" default="#333333" variants="#2b256f,#00b2b4,#4ba976,#696f00,#b38f00,#f07300,#d0422c,#f37a86,#7b5341" value="#000000"/> <Variable name="menu.font" description="Font" type="font" default="'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, Arial, sans-serif" value="'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, Arial, sans-serif"/> <Variable name="menu.text.color" description="Text Color" type="color" default="#FFFFFF" variants="#FFFFFF" value="#ffffff"/> </Group> <Group description="Links"> <Variable name="link.font" description="Link Text" type="font" default="'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, Arial, sans-serif" value="'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, Arial, sans-serif"/> <Variable name="link.color" description="Link Color" type="color" default="#009EB8" variants="#2b256f,#00b2b4,#4ba976,#696f00,#b38f00,#f07300,#d0422c,#f37a86,#7b5341" value="#5280e9"/> <Variable name="link.hover.color" description="Link Hover Color" type="color" default="#009EB8" variants="#2b256f,#00b2b4,#4ba976,#696f00,#b38f00,#f07300,#d0422c,#f37a86,#7b5341" value="#5280e9"/> <Variable name="link.visited.color" description="Link Visited Color" type="color" default="#009EB8" variants="#2b256f,#00b2b4,#4ba976,#696f00,#b38f00,#f07300,#d0422c,#f37a86,#7b5341" value="#5280e9"/> </Group> <Group description="Blog Title"> <Variable name="blog.title.font" description="Font" type="font" default="'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, Arial, sans-serif" value="normal bold 100% Impact, sans-serif"/> <Variable name="blog.title.color" description="Color" type="color" default="#555555" variants="#555555" value="#000000"/> </Group> <Group description="Blog Description"> <Variable name="blog.description.font" description="Font" type="font" default="'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, Arial, sans-serif" value="normal normal 100% Georgia, Utopia, 'Palatino Linotype', Palatino, serif"/> <Variable name="blog.description.color" description="Color" type="color" default="#555555" variants="#555555" value="#2c2c2c"/> </Group> <Group description="Post Title"> <Variable name="post.title.font" description="Font" type="font" default="'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, Arial, sans-serif" value="'Helvetica Neue Light', HelveticaNeue-Light, 'Helvetica Neue', Helvetica, Arial, sans-serif"/> <Variable name="post.title.color" description="Color" type="color" default="#333333" variants="#333333" value="#3f3f3f"/> </Group> <Group description="Date Ribbon"> <Variable name="ribbon.color" description="Color" type="color" default="#666666" variants="#2b256f,#00b2b4,#4ba976,#696f00,#b38f00,#f07300,#d0422c,#f37a86,#7b5341" value="#3f3f3f"/> <Variable name="ribbon.hover.color" description="Hover Color" type="color" default="#AD3A2B" variants="#AD3A2B" value="#7b691e"/> </Group> <Variable name="blitzview" description="Initial view type" type="automatic" default="sidebar" value="sidebar"/> */ /* BEGIN CUT */ { "font:Text": "$(page.text.font)", "color:Text": "$(page.text.color)", "image:Background": "$(body.background)", "color:Background": "$(body.background.color)", "color:Header Background": "$(header.background.color)", "color:Primary": "$(primary.color)", "color:Menu Text": "$(menu.text.color)", "font:Menu": "$(menu.font)", "font:Link": "$(link.font)", "color:Link": "$(link.color)", "color:Link Visited": "$(link.visited.color)", "color:Link Hover": "$(link.hover.color)", "font:Blog Title": "$(blog.title.font)", "color:Blog Title": "$(blog.title.color)", "font:Blog Description": "$(blog.description.font)", "color:Blog Description": "$(blog.description.color)", "font:Post Title": "$(post.title.font)", "color:Post Title": "$(post.title.color)", "color:Ribbon": "$(ribbon.color)", "color:Ribbon Hover": "$(ribbon.hover.color)", "view": "$blitzview" } /* END CUT */ ]]></b:skin> <b:template-skin> <b:variable default='960px' name='content.width' type='length'/> <b:variable default='0' name='main.column.left.width' type='length'/> <b:variable default='310px' name='main.column.right.width' type='length'/> <b:variable default='46px' name='faviconpanel.height' type='length'/> <![CDATA[ body { min-width: $(content.width); } .column-center-outer { margin-top: $(faviconpanel.height); } .content-outer, .content-fauxcolumn-outer, .region-inner { min-width: $(content.width); max-width: $(content.width); _width: $(content.width); } .main-inner .columns { padding-left: $(main.column.left.width); padding-right: $(main.column.right.width); } .main-inner .fauxcolumn-center-outer { left: $(main.column.left.width); right: $(main.column.right.width); /* IE6 does not respect left and right together */ _width: expression(this.parentNode.offsetWidth - parseInt("$(main.column.left.width)") - parseInt("$(main.column.right.width)") + 'px'); } .main-inner .fauxcolumn-left-outer { width: $(main.column.left.width); } .main-inner .fauxcolumn-right-outer { width: $(main.column.right.width); } .main-inner .column-left-outer { width: $(main.column.left.width); right: 100%; margin-left: -$(main.column.left.width); } .main-inner .column-right-outer { width: $(main.column.right.width); margin-right: -$(main.column.right.width); } #layout { min-width: 0; } #layout .content-outer { min-width: 0; width: 800px; } #layout .region-inner { min-width: 0; width: auto; } ]]> </b:template-skin> <script expr:src='data:blog.dynamicViewsScriptSrc + &quot;/js/thirdparty/jquery.js&quot;' type='text/javascript'/> <script expr:src='data:blog.dynamicViewsScriptSrc + &quot;/js/thirdparty/jquery-mousewheel.js&quot;' type='text/javascript'/> <script expr:src='data:blog.dynamicViewsScriptSrc + &quot;/js/common.js&quot;' type='text/javascript'/> <b:if cond='data:blog.localeUnderscoreDelimited != &quot;en&quot;'> <script expr:src='data:blog.dynamicViewsScriptSrc + &quot;/js/languages/lang__&quot; + data:blog.localeUnderscoreDelimited + &quot;.js&quot;' type='text/javascript'/> </b:if> <b:if cond='data:blog.view'> <script expr:src='data:blog.dynamicViewsScriptSrc + &quot;/js/&quot; + data:blog.view + &quot;.js&quot;' type='text/javascript'/> <b:else/> <b:if cond='data:blog.isMobileRequest'> <script expr:src='data:blog.dynamicViewsScriptSrc + &quot;/js/classic.js&quot;' type='text/javascript'/> <b:else/> <b:if cond='data:skin.vars.blitzview'> <script expr:src='data:blog.dynamicViewsScriptSrc + &quot;/js/&quot; + data:skin.vars.blitzview + &quot;.js&quot;' type='text/javascript'/> <b:else/> <script expr:src='data:blog.dynamicViewsScriptSrc + &quot;/js/sidebar.js&quot;' type='text/javascript'/> </b:if> </b:if> </b:if> <script expr:src='data:blog.dynamicViewsScriptSrc + &quot;/js/gadgets.js&quot;' type='text/javascript'/> <script expr:src='data:blog.dynamicViewsCommentsSrc'/> <b:include data='blog' name='google-analytics'/> </head> <body> <div class='content'> <div class='content-outer'> <div class='fauxborder-left content-fauxborder-left'> <div class='content-inner'> <div class='main-outer'> <div class='fauxborder-left main-fauxborder-left'> <div class='region-inner main-inner'> <div class='columns fauxcolumns'> <div class='column-center-outer'> <div class='column-center-inner'> <b:section class='main' id='main' showaddelement='no'> <b:widget id='Blog1' locked='false' title='Blog Posts' type='Blog'> <b:includable id='main' var='top'> <b:if cond='data:mobile == &quot;false&quot;'> <!-- posts --> <div class='blog-posts hfeed'> <b:include data='top' name='status-message'/> <data:defaultAdStart/> <b:loop values='data:posts' var='post'> <b:if cond='data:post.isDateStart'> <b:if cond='data:post.isFirstPost == &quot;false&quot;'> &lt;/div&gt;&lt;/div&gt; </b:if> </b:if> <b:if cond='data:post.isDateStart'> &lt;div class=&quot;date-outer&quot;&gt; </b:if> <b:if cond='data:post.dateHeader'> <h2 class='date-header'><span><data:post.dateHeader/></span></h2> </b:if> <b:if cond='data:post.isDateStart'> &lt;div class=&quot;date-posts&quot;&gt; </b:if> <div class='post-outer'> <b:include data='post' name='post'/> <b:if cond='data:blog.pageType == &quot;static_page&quot;'> <b:include data='post' name='comment_picker'/> </b:if> <b:if cond='data:blog.pageType == &quot;item&quot;'> <b:include data='post' name='comment_picker'/> </b:if> </div> <b:if cond='data:post.includeAd'> <b:if cond='data:post.isFirstPost'> <data:defaultAdEnd/> <b:else/> <data:adEnd/> </b:if> <div class='inline-ad'> <data:adCode/> </div> <data:adStart/> </b:if> </b:loop> <b:if cond='data:numPosts != 0'> &lt;/div&gt;&lt;/div&gt; </b:if> <data:adEnd/> </div> <!-- navigation --> <b:include name='nextprev'/> <!-- feed links --> <b:include name='feedLinks'/> <b:else/> <b:include name='mobile-main'/> </b:if> <b:if cond='data:top.showDummy'> <data:top.dummyBootstrap/> </b:if> </b:includable> <b:includable id='backlinkDeleteIcon' var='backlink'> <span expr:class='&quot;item-control &quot; + data:backlink.adminClass'> <a expr:href='data:backlink.deleteUrl' expr:title='data:top.deleteBacklinkMsg'> <img src='//www.blogger.com/img/icon_delete13.gif'/> </a> </span> </b:includable> <b:includable id='backlinks' var='post'> <a name='links'/><h4><data:post.backlinksLabel/></h4> <b:if cond='data:post.numBacklinks != 0'> <dl class='comments-block' id='comments-block'> <b:loop values='data:post.backlinks' var='backlink'> <div class='collapsed-backlink backlink-control'> <dt class='comment-title'> <span class='backlink-toggle-zippy'>&#160;</span> <a expr:href='data:backlink.url' rel='nofollow'><data:backlink.title/></a> <b:include data='backlink' name='backlinkDeleteIcon'/> </dt> <dd class='comment-body collapseable'> <data:backlink.snippet/> </dd> <dd class='comment-footer collapseable'> <span class='comment-author'><data:post.authorLabel/> <data:backlink.author/></span> <span class='comment-timestamp'><data:post.timestampLabel/> <data:backlink.timestamp/></span> </dd> </div> </b:loop> </dl> </b:if> <p class='comment-footer'> <a class='comment-link' expr:href='data:post.createLinkUrl' expr:id='data:widget.instanceId + &quot;_backlinks-create-link&quot;' target='_blank'><data:post.createLinkLabel/></a> </p> </b:includable> <b:includable id='comment-form' var='post'> <div class='comment-form'> <a name='comment-form'/> <b:if cond='data:mobile'> <h4 id='comment-post-message'> <a expr:id='data:widget.instanceId + &quot;_comment-editor-toggle-link&quot;' href='javascript:void(0)'><data:postCommentMsg/></a></h4> <p><data:blogCommentMessage/></p> <data:blogTeamBlogMessage/> <a expr:href='data:post.commentFormIframeSrc' id='comment-editor-src'/> <iframe allowtransparency='true' class='blogger-iframe-colorize blogger-comment-from-post' frameborder='0' height='410' id='comment-editor' name='comment-editor' src='' style='display: none' width='100%'/> <b:else/> <h4 id='comment-post-message'><data:postCommentMsg/></h4> <p><data:blogCommentMessage/></p> <data:blogTeamBlogMessage/> <a expr:href='data:post.commentFormIframeSrc' id='comment-editor-src'/> <iframe allowtransparency='true' class='blogger-iframe-colorize blogger-comment-from-post' frameborder='0' height='410' id='comment-editor' name='comment-editor' src='' width='100%'/> </b:if> <data:post.friendConnectJs/> <data:post.cmtfpIframe/> <script type='text/javascript'> BLOG_CMT_createIframe(&#39;<data:post.appRpcRelayPath/>&#39;); </script> </div> </b:includable> <b:includable id='commentDeleteIcon' var='comment'> <span expr:class='&quot;item-control &quot; + data:comment.adminClass'> <b:if cond='data:showCmtPopup'> <div class='goog-toggle-button'> <div class='goog-inline-block comment-action-icon'/> </div> <b:else/> <a class='comment-delete' expr:href='data:comment.deleteUrl' expr:title='data:top.deleteCommentMsg'> <img src='//www.blogger.com/img/icon_delete13.gif'/> </a> </b:if> </span> </b:includable> <b:includable id='comment_count_picker' var='post'> <b:if cond='data:post.commentSource == 1'> <span class='cmt_count_iframe_holder' expr:data-count='data:post.numComments' expr:data-onclick='data:post.addCommentOnclick' expr:data-post-url='data:post.url' expr:data-url='data:post.canonicalUrl'> </span> <b:else/> <a class='comment-link' expr:href='data:post.addCommentUrl' expr:onclick='data:post.addCommentOnclick'> <data:post.commentLabelFull/>: </a> </b:if> </b:includable> <b:includable id='comment_picker' var='post'> <b:if cond='data:post.commentSource == 1'> <b:include data='post' name='iframe_comments'/> <b:else/> <b:if cond='data:post.showThreadedComments'> <b:include data='post' name='threaded_comments'/> <b:else/> <b:include data='post' name='comments'/> </b:if> </b:if> </b:includable> <b:includable id='comments' var='post'> <div class='comments' id='comments'> <a name='comments'/> <b:if cond='data:post.allowComments'> <h4><data:post.commentLabelFull/>:</h4> <b:if cond='data:post.commentPagingRequired'> <span class='paging-control-container'> <b:if cond='data:post.hasOlderLinks'> <a expr:class='data:post.oldLinkClass' expr:href='data:post.oldestLinkUrl'><data:post.oldestLinkText/></a> &#160; <a expr:class='data:post.oldLinkClass' expr:href='data:post.olderLinkUrl'><data:post.olderLinkText/></a> &#160; </b:if> <data:post.commentRangeText/> <b:if cond='data:post.hasNewerLinks'> &#160; <a expr:class='data:post.newLinkClass' expr:href='data:post.newerLinkUrl'><data:post.newerLinkText/></a> &#160; <a expr:class='data:post.newLinkClass' expr:href='data:post.newestLinkUrl'><data:post.newestLinkText/></a> </b:if> </span> </b:if> <div expr:id='data:widget.instanceId + &quot;_comments-block-wrapper&quot;'> <dl expr:class='data:post.avatarIndentClass' id='comments-block'> <b:loop values='data:post.comments' var='comment'> <dt expr:class='&quot;comment-author &quot; + data:comment.authorClass' expr:id='data:comment.anchorName'> <b:if cond='data:comment.favicon'> <img expr:src='data:comment.favicon' height='16px' style='margin-bottom:-2px;' width='16px'/> </b:if> <a expr:name='data:comment.anchorName'/> <b:if cond='data:blog.enabledCommentProfileImages'> <data:comment.authorAvatarImage/> </b:if> <b:if cond='data:comment.authorUrl'> <a expr:href='data:comment.authorUrl' rel='nofollow'><data:comment.author/></a> <b:else/> <data:comment.author/> </b:if> <data:commentPostedByMsg/> </dt> <dd class='comment-body' expr:id='data:widget.instanceId + data:comment.cmtBodyIdPostfix'> <b:if cond='data:comment.isDeleted'> <span class='deleted-comment'><data:comment.body/></span> <b:else/> <p> <data:comment.body/> </p> </b:if> </dd> <dd class='comment-footer'> <span class='comment-timestamp'> <a expr:href='data:comment.url' title='comment permalink'> <data:comment.timestamp/> </a> <b:include data='comment' name='commentDeleteIcon'/> </span> </dd> </b:loop> </dl> </div> <b:if cond='data:post.commentPagingRequired'> <span class='paging-control-container'> <a expr:class='data:post.oldLinkClass' expr:href='data:post.oldestLinkUrl'> <data:post.oldestLinkText/> </a> <a expr:class='data:post.oldLinkClass' expr:href='data:post.olderLinkUrl'> <data:post.olderLinkText/> </a> &#160; <data:post.commentRangeText/> &#160; <a expr:class='data:post.newLinkClass' expr:href='data:post.newerLinkUrl'> <data:post.newerLinkText/> </a> <a expr:class='data:post.newLinkClass' expr:href='data:post.newestLinkUrl'> <data:post.newestLinkText/> </a> </span> </b:if> <p class='comment-footer'> <b:if cond='data:post.embedCommentForm'> <b:if cond='data:post.allowNewComments'> <b:include data='post' name='comment-form'/> <b:else/> <data:post.noNewCommentsText/> </b:if> <b:else/> <b:if cond='data:post.allowComments'> <a expr:href='data:post.addCommentUrl' expr:onclick='data:post.addCommentOnclick'><data:postCommentMsg/></a> </b:if> </b:if> </p> </b:if> <b:if cond='data:showCmtPopup'> <div id='comment-popup'> <iframe allowtransparency='true' frameborder='0' id='comment-actions' name='comment-actions' scrolling='no'> </iframe> </div> </b:if> <div id='backlinks-container'> <div expr:id='data:widget.instanceId + &quot;_backlinks-container&quot;'> <b:if cond='data:post.showBacklinks'> <b:include data='post' name='backlinks'/> </b:if> </div> </div> </div> </b:includable> <b:includable id='feedLinks'> <b:if cond='data:blog.pageType != &quot;item&quot;'> <!-- Blog feed links --> <b:if cond='data:feedLinks'> <div class='blog-feeds'> <b:include data='feedLinks' name='feedLinksBody'/> </div> </b:if> <b:else/> <!--Post feed links --> <div class='post-feeds'> <b:loop values='data:posts' var='post'> <b:if cond='data:post.allowComments'> <b:if cond='data:post.feedLinks'> <b:include data='post.feedLinks' name='feedLinksBody'/> </b:if> </b:if> </b:loop> </div> </b:if> </b:includable> <b:includable id='feedLinksBody' var='links'> <div class='feed-links'> <data:feedLinksMsg/> <b:loop values='data:links' var='f'> <a class='feed-link' expr:href='data:f.url' expr:type='data:f.mimeType' target='_blank'><data:f.name/> (<data:f.feedType/>)</a> </b:loop> </div> </b:includable> <b:includable id='iframe_comments' var='post'> <b:if cond='data:post.allowIframeComments'> <script expr:src='data:post.iframeCommentSrc' type='text/javascript'/> <div class='cmt_iframe_holder' expr:data-href='data:post.canonicalUrl' expr:data-viewtype='data:post.viewType'/> <b:if cond='data:post.embedCommentForm == &quot;false&quot;'> <a expr:href='data:post.addCommentUrl' expr:onclick='data:post.addCommentOnclick'><data:postCommentMsg/></a> </b:if> </b:if> </b:includable> <b:includable id='mobile-index-post' var='post'> <div class='mobile-date-outer date-outer'> <b:if cond='data:post.dateHeader'> <div class='date-header'> <span><data:post.dateHeader/></span> </div> </b:if> <div class='mobile-post-outer'> <a expr:href='data:post.url'> <h3 class='mobile-index-title entry-title' itemprop='name'> <data:post.title/> </h3> <div class='mobile-index-arrow'>&amp;rsaquo;</div> <div class='mobile-index-contents'> <b:if cond='data:post.thumbnailUrl'> <div class='mobile-index-thumbnail'> <div class='Image'> <img expr:src='data:post.thumbnailUrl'/> </div> </div> </b:if> <div class='post-body'> <b:if cond='data:post.snippet'><data:post.snippet/></b:if> </div> </div> <div style='clear: both;'/> </a> <div class='mobile-index-comment'> <b:if cond='data:blog.pageType != &quot;static_page&quot;'> <b:if cond='data:post.allowComments'> <b:if cond='data:post.numComments != 0'> <b:include data='post' name='comment_count_picker'/> </b:if> </b:if> </b:if> </div> </div> </div> </b:includable> <b:includable id='mobile-main' var='top'> <!-- posts --> <div class='blog-posts hfeed'> <b:include data='top' name='status-message'/> <b:if cond='data:blog.pageType == &quot;index&quot;'> <b:loop values='data:posts' var='post'> <b:include data='post' name='mobile-index-post'/> </b:loop> <b:else/> <b:loop values='data:posts' var='post'> <b:include data='post' name='mobile-post'/> </b:loop> </b:if> </div> <b:include name='mobile-nextprev'/> </b:includable> <b:includable id='mobile-nextprev'> <div class='blog-pager' id='blog-pager'> <b:if cond='data:newerPageUrl'> <div class='mobile-link-button' id='blog-pager-newer-link'> <a class='blog-pager-newer-link' expr:href='data:newerPageUrl' expr:id='data:widget.instanceId + &quot;_blog-pager-newer-link&quot;' expr:title='data:newerPageTitle'>&amp;lsaquo;</a> </div> </b:if> <b:if cond='data:olderPageUrl'> <div class='mobile-link-button' id='blog-pager-older-link'> <a class='blog-pager-older-link' expr:href='data:olderPageUrl' expr:id='data:widget.instanceId + &quot;_blog-pager-older-link&quot;' expr:title='data:olderPageTitle'>&amp;rsaquo;</a> </div> </b:if> <div class='mobile-link-button' id='blog-pager-home-link'> <a class='home-link' expr:href='data:blog.homepageUrl'><data:homeMsg/></a> </div> <div class='mobile-desktop-link'> <a class='home-link' expr:href='data:desktopLinkUrl'><data:desktopLinkMsg/></a> </div> </div> <div class='clear'/> </b:includable> <b:includable id='mobile-post' var='post'> <div class='date-outer'> <b:if cond='data:post.dateHeader'> <h2 class='date-header'><span><data:post.dateHeader/></span></h2> </b:if> <div class='date-posts'> <div class='post-outer'> <div class='post hentry uncustomized-post-template' itemscope='itemscope' itemtype='http://schema.org/BlogPosting'> <b:if cond='data:post.thumbnailUrl'> <meta expr:content='data:post.thumbnailUrl' itemprop='image_url'/> </b:if> <meta expr:content='data:blog.blogId' itemprop='blogId'/> <meta expr:content='data:post.id' itemprop='postId'/> <a expr:name='data:post.id'/> <b:if cond='data:post.title'> <h3 class='post-title entry-title' itemprop='name'> <b:if cond='data:post.link'> <a expr:href='data:post.link'><data:post.title/></a> <b:else/> <b:if cond='data:post.url'> <b:if cond='data:blog.url != data:post.url'> <a expr:href='data:post.url'><data:post.title/></a> <b:else/> <data:post.title/> </b:if> <b:else/> <data:post.title/> </b:if> </b:if> </h3> </b:if> <div class='post-header'> <div class='post-header-line-1'/> </div> <div class='post-body entry-content' expr:id='&quot;post-body-&quot; + data:post.id' itemprop='articleBody'> <data:post.body/> <div style='clear: both;'/> <!-- clear for photos floats --> </div> <div class='post-footer'> <div class='post-footer-line post-footer-line-1'> <span class='post-author vcard'> <b:if cond='data:top.showAuthor'> <b:if cond='data:post.authorProfileUrl'> <span class='fn' itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta expr:content='data:post.authorProfileUrl' itemprop='url'/> <a expr:href='data:post.authorProfileUrl' rel='author' title='author profile'> <span itemprop='name'><data:post.author/></span> </a> </span> <b:else/> <span class='fn' itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <span itemprop='name'><data:post.author/></span> </span> </b:if> </b:if> </span> <span class='post-timestamp'> <b:if cond='data:top.showTimestamp'> <data:top.timestampLabel/> <b:if cond='data:post.url'> <meta expr:content='data:post.canonicalUrl' itemprop='url'/> <a class='timestamp-link' expr:href='data:post.url' rel='bookmark' title='permanent link'><abbr class='published' expr:title='data:post.timestampISO8601' itemprop='datePublished'><data:post.timestamp/></abbr></a> </b:if> </b:if> </span> <span class='post-comment-link'> <b:if cond='data:blog.pageType != &quot;item&quot;'> <b:if cond='data:blog.pageType != &quot;static_page&quot;'> <b:if cond='data:post.allowComments'> <b:include data='post' name='comment_count_picker'/> </b:if> </b:if> </b:if> </span> </div> <div class='post-footer-line post-footer-line-2'> <b:if cond='data:top.showMobileShare'> <div class='mobile-link-button goog-inline-block' id='mobile-share-button'> <a href='javascript:void(0);'><data:shareMsg/></a> </div> </b:if> <b:if cond='data:top.showDummy'> <div class='goog-inline-block dummy-container'><data:post.dummyTag/></div> </b:if> </div> </div> </div> <b:if cond='data:blog.pageType == &quot;static_page&quot;'> <b:include data='post' name='comment_picker'/> </b:if> <b:if cond='data:blog.pageType == &quot;item&quot;'> <b:include data='post' name='comment_picker'/> </b:if> </div> </div> </div> </b:includable> <b:includable id='nextprev'> <div class='blog-pager' id='blog-pager'> <b:if cond='data:newerPageUrl'> <span id='blog-pager-newer-link'> <a class='blog-pager-newer-link' expr:href='data:newerPageUrl' expr:id='data:widget.instanceId + &quot;_blog-pager-newer-link&quot;' expr:title='data:newerPageTitle'><data:newerPageTitle/></a> </span> </b:if> <b:if cond='data:olderPageUrl'> <span id='blog-pager-older-link'> <a class='blog-pager-older-link' expr:href='data:olderPageUrl' expr:id='data:widget.instanceId + &quot;_blog-pager-older-link&quot;' expr:title='data:olderPageTitle'><data:olderPageTitle/></a> </span> </b:if> <a class='home-link' expr:href='data:blog.homepageUrl'><data:homeMsg/></a> <b:if cond='data:mobileLinkUrl'> <div class='blog-mobile-link'> <a expr:href='data:mobileLinkUrl'><data:mobileLinkMsg/></a> </div> </b:if> </div> <div class='clear'/> </b:includable> <b:includable id='post' var='post'> <div class='post hentry' itemprop='blogPost' itemscope='itemscope' itemtype='http://schema.org/BlogPosting'> <b:if cond='data:post.firstImageUrl'> <meta expr:content='data:post.firstImageUrl' itemprop='image_url'/> </b:if> <meta expr:content='data:blog.blogId' itemprop='blogId'/> <meta expr:content='data:post.id' itemprop='postId'/> <a expr:name='data:post.id'/> <b:if cond='data:post.title'> <h3 class='post-title entry-title' itemprop='name'> <b:if cond='data:post.link'> <a expr:href='data:post.link'><data:post.title/></a> <b:else/> <b:if cond='data:post.url'> <b:if cond='data:blog.url != data:post.url'> <a expr:href='data:post.url'><data:post.title/></a> <b:else/> <data:post.title/> </b:if> <b:else/> <data:post.title/> </b:if> </b:if> </h3> </b:if> <div class='post-header'> <div class='post-header-line-1'/> </div> <b:if cond='data:blog.metaDescription == &quot;&quot;'> <!-- Then use the post body as the schema.org description, for good G+/FB snippeting. --> <div class='post-body entry-content' expr:id='&quot;post-body-&quot; + data:post.id' itemprop='description articleBody'> <data:post.body/> <div style='clear: both;'/> <!-- clear for photos floats --> </div> <b:else/> <div class='post-body entry-content' expr:id='&quot;post-body-&quot; + data:post.id' itemprop='articleBody'> <data:post.body/> <div style='clear: both;'/> <!-- clear for photos floats --> </div> </b:if> <b:if cond='data:post.hasJumpLink'> <div class='jump-link'> <a expr:href='data:post.url + &quot;#more&quot;' expr:title='data:post.title'><data:post.jumpText/></a> </div> </b:if> <div class='post-footer'> <div class='post-footer-line post-footer-line-1'><span class='post-author vcard'> <b:if cond='data:top.showAuthor'> <data:top.authorLabel/> <b:if cond='data:post.authorProfileUrl'> <span class='fn' itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <meta expr:content='data:post.authorProfileUrl' itemprop='url'/> <a class='g-profile' expr:href='data:post.authorProfileUrl' rel='author' title='author profile'> <span itemprop='name'><data:post.author/></span> </a> </span> <b:else/> <span class='fn' itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <span itemprop='name'><data:post.author/></span> </span> </b:if> </b:if> </span> <span class='post-timestamp'> <b:if cond='data:top.showTimestamp'> <data:top.timestampLabel/> <b:if cond='data:post.url'> <meta expr:content='data:post.canonicalUrl' itemprop='url'/> <a class='timestamp-link' expr:href='data:post.url' rel='bookmark' title='permanent link'><abbr class='published' expr:title='data:post.timestampISO8601' itemprop='datePublished'><data:post.timestamp/></abbr></a> </b:if> </b:if> </span> <span class='post-comment-link'> <b:if cond='data:blog.pageType != &quot;item&quot;'> <b:if cond='data:blog.pageType != &quot;static_page&quot;'> <b:if cond='data:post.allowComments'> <b:include data='post' name='comment_count_picker'/> </b:if> </b:if> </b:if> </span> <span class='post-icons'> <!-- email post links --> <b:if cond='data:post.emailPostUrl'> <span class='item-action'> <a expr:href='data:post.emailPostUrl' expr:title='data:top.emailPostMsg'> <img alt='' class='icon-action' height='13' src='http://img1.blogblog.com/img/icon18_email.gif' width='18'/> </a> </span> </b:if> <!-- quickedit pencil --> <b:include data='post' name='postQuickEdit'/> </span> <div class='post-share-buttons goog-inline-block'> <b:if cond='data:post.sharePostUrl'> <b:include data='post' name='shareButtons'/> </b:if> </div> </div> <div class='post-footer-line post-footer-line-2'><span class='post-labels'> <b:if cond='data:post.labels'> <data:postLabelsLabel/> <b:loop values='data:post.labels' var='label'> <a expr:href='data:label.url' rel='tag'><data:label.name/></a><b:if cond='data:label.isLast != &quot;true&quot;'>,</b:if> </b:loop> </b:if> </span> </div> <div class='post-footer-line post-footer-line-3'><span class='post-location'> <b:if cond='data:top.showLocation'> <b:if cond='data:post.location'> <data:postLocationLabel/> <a expr:href='data:post.location.mapsUrl' target='_blank'><data:post.location.name/></a> </b:if> </b:if> </span> </div> <b:if cond='data:post.authorAboutMe'> <div class='author-profile' itemprop='author' itemscope='itemscope' itemtype='http://schema.org/Person'> <b:if cond='data:post.authorPhoto.url'> <img expr:src='data:post.authorPhoto.url' itemprop='image' width='50px'/> </b:if> <div> <a class='g-profile' expr:href='data:post.authorProfileUrl' itemprop='url' rel='author' title='author profile'> <span itemprop='name'><data:post.author/></span> </a> </div> <span itemprop='description'><data:post.authorAboutMe/></span> </div> </b:if> </div> </div> </b:includable> <b:includable id='postQuickEdit' var='post'> <b:if cond='data:post.editUrl'> <span expr:class='&quot;item-control &quot; + data:post.adminClass'> <a expr:href='data:post.editUrl' expr:title='data:top.editPostMsg'> <img alt='' class='icon-action' height='18' src='http://img2.blogblog.com/img/icon18_edit_allbkg.gif' width='18'/> </a> </span> </b:if> </b:includable> <b:includable id='shareButtons' var='post'> <b:if cond='data:top.showEmailButton'><a class='goog-inline-block share-button sb-email' expr:href='data:post.sharePostUrl + &quot;&amp;target=email&quot;' expr:title='data:top.emailThisMsg' target='_blank'><span class='share-button-link-text'><data:top.emailThisMsg/></span></a></b:if><b:if cond='data:top.showBlogThisButton'><a class='goog-inline-block share-button sb-blog' expr:href='data:post.sharePostUrl + &quot;&amp;target=blog&quot;' expr:onclick='&quot;window.open(this.href, \&quot;_blank\&quot;, \&quot;height=270,width=475\&quot;); return false;&quot;' expr:title='data:top.blogThisMsg' target='_blank'><span class='share-button-link-text'><data:top.blogThisMsg/></span></a></b:if><b:if cond='data:top.showTwitterButton'><a class='goog-inline-block share-button sb-twitter' expr:href='data:post.sharePostUrl + &quot;&amp;target=twitter&quot;' expr:title='data:top.shareToTwitterMsg' target='_blank'><span class='share-button-link-text'><data:top.shareToTwitterMsg/></span></a></b:if><b:if cond='data:top.showFacebookButton'><a class='goog-inline-block share-button sb-facebook' expr:href='data:post.sharePostUrl + &quot;&amp;target=facebook&quot;' expr:onclick='&quot;window.open(this.href, \&quot;_blank\&quot;, \&quot;height=430,width=640\&quot;); return false;&quot;' expr:title='data:top.shareToFacebookMsg' target='_blank'><span class='share-button-link-text'><data:top.shareToFacebookMsg/></span></a></b:if><b:if cond='data:top.showPinterestButton'><a class='goog-inline-block share-button sb-pinterest' expr:href='data:post.sharePostUrl + &quot;&amp;target=pinterest&quot;' expr:title='data:top.shareToPinterestMsg' target='_blank'><span class='share-button-link-text'><data:top.shareToPinterestMsg/></span></a></b:if><b:if cond='data:top.showDummy'><div class='goog-inline-block dummy-container'><data:post.dummyTag/></div></b:if> </b:includable> <b:includable id='status-message'> <b:if cond='data:navMessage'> <div class='status-msg-wrap'> <div class='status-msg-body'> <data:navMessage/> </div> <div class='status-msg-border'> <div class='status-msg-bg'> <div class='status-msg-hidden'><data:navMessage/></div> </div> </div> </div> <div style='clear: both;'/> </b:if> </b:includable> <b:includable id='threaded-comment-form' var='post'> <div class='comment-form'> <a name='comment-form'/> <b:if cond='data:mobile'> <p><data:blogCommentMessage/></p> <data:blogTeamBlogMessage/> <a expr:href='data:post.commentFormIframeSrc' id='comment-editor-src'/> <iframe allowtransparency='true' class='blogger-iframe-colorize blogger-comment-from-post' frameborder='0' height='410' id='comment-editor' name='comment-editor' src='' style='display: none' width='100%'/> <b:else/> <p><data:blogCommentMessage/></p> <data:blogTeamBlogMessage/> <a expr:href='data:post.commentFormIframeSrc' id='comment-editor-src'/> <iframe allowtransparency='true' class='blogger-iframe-colorize blogger-comment-from-post' frameborder='0' height='410' id='comment-editor' name='comment-editor' src='' width='100%'/> </b:if> <data:post.friendConnectJs/> <data:post.cmtfpIframe/> <script type='text/javascript'> BLOG_CMT_createIframe(&#39;<data:post.appRpcRelayPath/>&#39;); </script> </div> </b:includable> <b:includable id='threaded_comment_js' var='post'> <script async='async' expr:src='data:post.commentSrc' type='text/javascript'/> <script type='text/javascript'> (function() { var items = <data:post.commentJso/>; var msgs = <data:post.commentMsgs/>; var config = <data:post.commentConfig/>; // <![CDATA[ var cursor = null; if (items && items.length > 0) { cursor = parseInt(items[items.length - 1].timestamp) + 1; } var bodyFromEntry = function(entry) { if (entry.gd$extendedProperty) { for (var k in entry.gd$extendedProperty) { if (entry.gd$extendedProperty[k].name == 'blogger.contentRemoved') { return '<span class="deleted-comment">' + entry.content.$t + '</span>'; } } } return entry.content.$t; } var parse = function(data) { cursor = null; var comments = []; if (data && data.feed && data.feed.entry) { for (var i = 0, entry; entry = data.feed.entry[i]; i++) { var comment = {}; // comment ID, parsed out of the original id format var id = /blog-(\d+).post-(\d+)/.exec(entry.id.$t); comment.id = id ? id[2] : null; comment.body = bodyFromEntry(entry); comment.timestamp = Date.parse(entry.published.$t) + ''; if (entry.author && entry.author.constructor === Array) { var auth = entry.author[0]; if (auth) { comment.author = { name: (auth.name ? auth.name.$t : undefined), profileUrl: (auth.uri ? auth.uri.$t : undefined), avatarUrl: (auth.gd$image ? auth.gd$image.src : undefined) }; } } if (entry.link) { if (entry.link[2]) { comment.link = comment.permalink = entry.link[2].href; } if (entry.link[3]) { var pid = /.*comments\/default\/(\d+)\?.*/.exec(entry.link[3].href); if (pid && pid[1]) { comment.parentId = pid[1]; } } } comment.deleteclass = 'item-control blog-admin'; if (entry.gd$extendedProperty) { for (var k in entry.gd$extendedProperty) { if (entry.gd$extendedProperty[k].name == 'blogger.itemClass') { comment.deleteclass += ' ' + entry.gd$extendedProperty[k].value; } else if (entry.gd$extendedProperty[k].name == 'blogger.displayTime') { comment.displayTime = entry.gd$extendedProperty[k].value; } } } comments.push(comment); } } return comments; }; var paginator = function(callback) { if (hasMore()) { var url = config.feed + '?alt=json&v=2&orderby=published&reverse=false&max-results=50'; if (cursor) { url += '&published-min=' + new Date(cursor).toISOString(); } window.bloggercomments = function(data) { var parsed = parse(data); cursor = parsed.length < 50 ? null : parseInt(parsed[parsed.length - 1].timestamp) + 1 callback(parsed); window.bloggercomments = null; } url += '&callback=bloggercomments'; var script = document.createElement('script'); script.type = 'text/javascript'; script.src = url; document.getElementsByTagName('head')[0].appendChild(script); } }; var hasMore = function() { return !!cursor; }; var getMeta = function(key, comment) { if ('iswriter' == key) { var matches = !!comment.author && comment.author.name == config.authorName && comment.author.profileUrl == config.authorUrl; return matches ? 'true' : ''; } else if ('deletelink' == key) { return config.baseUri + '/delete-comment.g?blogID=' + config.blogId + '&postID=' + comment.id; } else if ('deleteclass' == key) { return comment.deleteclass; } return ''; }; var replybox = null; var replyUrlParts = null; var replyParent = undefined; var onReply = function(commentId, domId) { if (replybox == null) { // lazily cache replybox, and adjust to suit this style: replybox = document.getElementById('comment-editor'); if (replybox != null) { replybox.height = '250px'; replybox.style.display = 'block'; replyUrlParts = replybox.src.split('#'); } } if (replybox && (commentId !== replyParent)) { document.getElementById(domId).insertBefore(replybox, null); replybox.src = replyUrlParts[0] + (commentId ? '&parentID=' + commentId : '') + '#' + replyUrlParts[1]; replyParent = commentId; } }; var hash = (window.location.hash || '#').substring(1); var startThread, targetComment; if (/^comment-form_/.test(hash)) { startThread = hash.substring('comment-form_'.length); } else if (/^c[0-9]+$/.test(hash)) { targetComment = hash.substring(1); } // Configure commenting API: var configJso = { 'maxDepth': config.maxThreadDepth }; var provider = { 'id': config.postId, 'data': items, 'loadNext': paginator, 'hasMore': hasMore, 'getMeta': getMeta, 'onReply': onReply, 'rendered': true, 'initComment': targetComment, 'initReplyThread': startThread, 'config': configJso, 'messages': msgs }; var render = function() { if (window.goog && window.goog.comments) { var holder = document.getElementById('comment-holder'); window.goog.comments.render(holder, provider); } }; // render now, or queue to render when library loads: if (window.goog && window.goog.comments) { render(); } else { window.goog = window.goog || {}; window.goog.comments = window.goog.comments || {}; window.goog.comments.loadQueue = window.goog.comments.loadQueue || []; window.goog.comments.loadQueue.push(render); } })(); // ]]> </script> </b:includable> <b:includable id='threaded_comments' var='post'> <div class='comments' id='comments'> <a name='comments'/> <h4><data:post.commentLabelFull/>:</h4> <div class='comments-content'> <b:if cond='data:post.embedCommentForm'> <b:include data='post' name='threaded_comment_js'/> </b:if> <div id='comment-holder'> <data:post.commentHtml/> </div> </div> <p class='comment-footer'> <b:if cond='data:post.allowNewComments'> <b:include data='post' name='threaded-comment-form'/> <b:else/> <data:post.noNewCommentsText/> </b:if> </p> <b:if cond='data:showCmtPopup'> <div id='comment-popup'> <iframe allowtransparency='true' frameborder='0' id='comment-actions' name='comment-actions' scrolling='no'> </iframe> </div> </b:if> <div id='backlinks-container'> <div expr:id='data:widget.instanceId + &quot;_backlinks-container&quot;'> <b:if cond='data:post.showBacklinks'> <b:include data='post' name='backlinks'/> </b:if> </div> </div> </div> </b:includable> </b:widget> </b:section> </div> </div> <div class='column-right-outer'> <div class='column-right-inner'> <aside> <b:section class='sidebar' id='sidebar-right-1' preferred='yes' showaddelement='yes'> <b:widget id='Header1' locked='false' title='Adventures of An Amateur GameDev (Header)' type='Header'> <b:includable id='main'> <b:if cond='data:useImage'> <b:if cond='data:imagePlacement == &quot;BEHIND&quot;'> <!-- Show image as background to text. You can't really calculate the width reliably in JS because margins are not taken into account by any of clientWidth, offsetWidth or scrollWidth, so we don't force a minimum width if the user is using shrink to fit. This results in a margin-width's worth of pixels being cropped. If the user is not using shrink to fit then we expand the header. --> <b:if cond='data:mobile'> <div id='header-inner'> <div class='titlewrapper' style='background: transparent'> <h1 class='title' style='background: transparent; border-width: 0px'> <b:include name='title'/> </h1> </div> <b:include name='description'/> </div> <b:else/> <div expr:style='&quot;background-image: url(\&quot;&quot; + data:sourceUrl + &quot;\&quot;); &quot; + &quot;background-position: &quot; + data:backgroundPositionStyleStr + &quot;; &quot; + data:widthStyleStr + &quot;min-height: &quot; + data:height + &quot;_height: &quot; + data:height + &quot;background-repeat: no-repeat; &quot;' id='header-inner'> <div class='titlewrapper' style='background: transparent'> <h1 class='title' style='background: transparent; border-width: 0px'> <b:include name='title'/> </h1> </div> <b:include name='description'/> </div> </b:if> <b:else/> <!--Show the image only--> <div id='header-inner'> <a expr:href='data:blog.homepageUrl' style='display: block'> <img expr:alt='data:title' expr:height='data:height' expr:id='data:widget.instanceId + &quot;_headerimg&quot;' expr:src='data:sourceUrl' expr:width='data:width' style='display: block'/> </a> <!--Show the description--> <b:if cond='data:imagePlacement == &quot;BEFORE_DESCRIPTION&quot;'> <b:include name='description'/> </b:if> </div> </b:if> <b:else/> <!--No header image --> <div id='header-inner'> <div class='titlewrapper'> <h1 class='title'> <b:include name='title'/> </h1> </div> <b:include name='description'/> </div> </b:if> </b:includable> <b:includable id='description'> <div class='descriptionwrapper'> <p class='description'><span><data:description/></span></p> </div> </b:includable> <b:includable id='title'> <b:if cond='data:blog.url == data:blog.homepageUrl'> <data:title/> <b:else/> <a expr:href='data:blog.homepageUrl'><data:title/></a> </b:if> </b:includable> </b:widget> <b:widget id='BlogArchive1' locked='false' title='Blog Archive' type='BlogArchive'> <b:includable id='main'> <b:if cond='data:title'> <h2><data:title/></h2> </b:if> <div class='widget-content'> <div id='ArchiveList'> <div expr:id='data:widget.instanceId + &quot;_ArchiveList&quot;'> <b:if cond='data:style == &quot;HIERARCHY&quot;'> <b:include data='data' name='interval'/> </b:if> <b:if cond='data:style == &quot;FLAT&quot;'> <b:include data='data' name='flat'/> </b:if> <b:if cond='data:style == &quot;MENU&quot;'> <b:include data='data' name='menu'/> </b:if> </div> </div> <b:include name='quickedit'/> </div> </b:includable> <b:includable id='flat' var='data'> <ul class='flat'> <b:loop values='data:data' var='i'> <li class='archivedate'> <a expr:href='data:i.url'><data:i.name/></a> (<data:i.post-count/>) </li> </b:loop> </ul> </b:includable> <b:includable id='interval' var='intervalData'> <b:loop values='data:intervalData' var='i'> <ul class='hierarchy'> <li expr:class='&quot;archivedate &quot; + data:i.expclass'> <b:include data='i' name='toggle'/> <a class='post-count-link' expr:href='data:i.url'><data:i.name/></a> <span class='post-count' dir='ltr'>(<data:i.post-count/>)</span> <b:if cond='data:i.data'> <b:include data='i.data' name='interval'/> </b:if> <b:if cond='data:i.posts'> <b:include data='i.posts' name='posts'/> </b:if> </li> </ul> </b:loop> </b:includable> <b:includable id='menu' var='data'> <select expr:id='data:widget.instanceId + &quot;_ArchiveMenu&quot;'> <option value=''><data:title/></option> <b:loop values='data:data' var='i'> <option expr:value='data:i.url'><data:i.name/> (<data:i.post-count/>)</option> </b:loop> </select> </b:includable> <b:includable id='posts' var='posts'> <ul class='posts'> <b:loop values='data:posts' var='i'> <li><a expr:href='data:i.url'><data:i.title/></a></li> </b:loop> </ul> </b:includable> <b:includable id='toggle' var='interval'> <b:if cond='data:interval.toggleId'> <b:if cond='data:interval.expclass == &quot;expanded&quot;'> <a class='toggle' href='javascript:void(0)'> <span class='zippy toggle-open'>&#9660;&#160;</span> </a> <b:else/> <a class='toggle' href='javascript:void(0)'> <span class='zippy'> <b:if cond='data:blog.languageDirection == &quot;rtl&quot;'> &#9668;&#160; <b:else/> &#9658;&#160; </b:if> </span> </a> </b:if> </b:if> </b:includable> </b:widget> <b:widget id='Profile1' locked='false' title='About Me' type='Profile'> <b:includable id='main'> <b:if cond='data:title != &quot;&quot;'> <h2><data:title/></h2> </b:if> <div class='widget-content'> <b:if cond='data:team == &quot;true&quot;'> <!-- team blog profile --> <ul> <b:loop values='data:authors' var='i'> <li><a class='profile-name-link g-profile' expr:href='data:i.userUrl' expr:style='&quot;background-image: url(&quot; + data:i.profileLogo + &quot;);&quot;'><data:i.display-name/></a></li> </b:loop> </ul> <b:else/> <!-- normal blog profile --> <b:if cond='data:photo.url != &quot;&quot;'> <a expr:href='data:userUrl'><img class='profile-img' expr:alt='data:photo.alt' expr:height='data:photo.height' expr:src='data:photo.url' expr:width='data:photo.width'/></a> </b:if> <dl class='profile-datablock'> <dt class='profile-data'> <a class='profile-name-link g-profile' expr:href='data:userUrl' expr:style='&quot;background-image: url(&quot; + data:profileLogo + &quot;);&quot;' rel='author'> <data:displayname/> </a> <b:if cond='data:hasgoogleprofile'> <br/> <div class='g-follow' data-annotation='bubble' data-height='20' expr:data-href='data:userUrl'/> </b:if> </dt> <b:if cond='data:showlocation == &quot;true&quot;'> <dd class='profile-data'><data:location/></dd> </b:if> <b:if cond='data:aboutme != &quot;&quot;'><dd class='profile-textblock'><data:aboutme/></dd></b:if> </dl> <a class='profile-link' expr:href='data:userUrl' rel='author'><data:viewProfileMsg/></a> </b:if> <b:include name='quickedit'/> </div> </b:includable> </b:widget> <b:widget id='Attribution1' locked='false' title='' type='Attribution'> <b:includable id='main'> <b:if cond='data:feedbackSurveyLink'> <div class='mobile-survey-link' style='text-align: center;'> <data:feedbackSurveyLink/> </div> </b:if> <div class='widget-content' style='text-align: center;'> <b:if cond='data:attribution != &quot;&quot;'> <data:attribution/> </b:if> </div> <b:include name='quickedit'/> </b:includable> </b:widget> <b:widget id='Navbar1' locked='false' title='Navbar' type='Navbar'> <b:includable id='main'>&lt;script type=&quot;text/javascript&quot;&gt; function setAttributeOnload(object, attribute, val) { if(window.addEventListener) { window.addEventListener(&#39;load&#39;, function(){ object[attribute] = val; }, false); } else { window.attachEvent(&#39;onload&#39;, function(){ object[attribute] = val; }); } } &lt;/script&gt; &lt;div id=&quot;navbar-iframe-container&quot;&gt;&lt;/div&gt; &lt;script type=&quot;text/javascript&quot; src=&quot;https://apis.google.com/js/plusone.js&quot;&gt;&lt;/script&gt; &lt;script type=&quot;text/javascript&quot;&gt; gapi.load(&quot;gapi.iframes:gapi.iframes.style.bubble&quot;, function() { if (gapi.iframes &amp;&amp; gapi.iframes.getContext) { gapi.iframes.getContext().openChild({ url: &#39;https://www.blogger.com/navbar.g?targetBlogID\0753223852017784708240\46blogName\75Adventures+of+An+Amateur+GameDev\46publishMode\75PUBLISH_MODE_BLOGSPOT\46navbarType\75LIGHT\46layoutType\75LAYOUTS\46searchRoot\75http://aaagd.blogspot.com/search\46blogLocale\75en\46v\0752\46homepageUrl\75http://aaagd.blogspot.com/\46vt\075679596904035941015&#39;, where: document.getElementById(&quot;navbar-iframe-container&quot;), id: &quot;navbar-iframe&quot; }); } }); &lt;/script&gt;&lt;script type=&quot;text/javascript&quot;&gt; (function() { var script = document.createElement(&#39;script&#39;); script.type = &#39;text/javascript&#39;; script.src = &#39;//pagead2.googlesyndication.com/pagead/js/google_top_exp.js&#39;; var head = document.getElementsByTagName(&#39;head&#39;)[0]; if (head) { head.appendChild(script); }})(); &lt;/script&gt; </b:includable> </b:widget> </b:section> </aside> </div> </div> <div style='clear: both'/> </div> </div> </div> </div> </div> </div> </div> </div> <script language='javascript' type='text/javascript'> setTimeout(function() { blogger.ui().configure().view(); }, 0); </script> </body> </html>Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_PUBLISHING_MODE2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00The type of publishing done for this blog.PUBLISH_MODE_BLOGSPOTMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_ADMIN_PERMISSION2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00The list of administrators' emails for the blog.mg.greenfield2@gmail.comMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_ADULT_CONTENT2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Whether this blog contains adult contentfalseMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_ALTERNATE_JSRENDER_ALLOWED2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Whether alternate JS renderings are allowedtrueMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_ANALYTICS_ACCOUNT_NUMBER2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Blog's Google Analytics account numberMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_ARCHIVE_DATE_FORMAT2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00The number of the archive index date format9Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_ARCHIVE_FREQUENCY2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00How frequently this blog should be archivedMONTHLYMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_AUTHOR_PERMISSION2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00The list of authors' emails who have permission to publish.Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_BACKLINKS_ALLOWED2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Whether to show comment backlinks on the blogfalseMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_BY_POST_ARCHIVING2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Whether to provide an archive page for each posttrueMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_COMMENT_ACCESS2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Who can commentANYONEMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_COMMENT_CAPTCHA2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Whether to require commenters to complete a CaptchatrueMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_COMMENT_EMAIL2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00List of e-mail addresses to send notifications of new comments tomg.greenfield2@gmail.comMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_COMMENT_FEED2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00The type of feed to provide for blog commentsFULLMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_COMMENT_FORM_LOCATION2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Blog comment form locationEMBEDDED_IFRAMEMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_COMMENT_MESSAGE2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Blog comment messageMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_COMMENT_MODERATION2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Whether to enable comment moderationENABLEDMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_COMMENT_MODERATION_DELAY2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Number of days after which new comments are subject to moderation14Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_COMMENT_MODERATION_EMAIL2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Email address to send notifications of new comments needing moderation tomg.greenfield2@gmail.comMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_COMMENT_PROFILE_IMAGES2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Whether to show profile images in commentstrueMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_COMMENTS_ALLOWED2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Whether to show commentstrueMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_COMMENTS_TIME_STAMP_FORMAT2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Comment time stamp format number29Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_CONVERT_LINE_BREAKS2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Whether to convert line breaks into <br /> tags in post editortrueMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_CUSTOM_PAGE_NOT_FOUND2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00The content served when the requested post or page is not found.Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_CUSTOM_ROBOTS_TXT2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00The custom robots.txt content of the blog served to search engines.Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_CUSTOM_ROBOTS_TXT_ENABLED2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Whether this blog serves custom robots.txt content to search engines.falseMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_DATE_FORMAT2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00The number of the date header format26Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_DEFAULT_BACKLINKS_MODE2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Default backlinks mode for postsDEFAULT_HAVE_BACKLINKSMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_DEFAULT_COMMENTS_MODE2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Default comment mode for postsDEFAULT_HAVE_COMMENTSMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_DESCRIPTION2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00A description of the blogA look into the mind and methods of an aspiring videogame developer.Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_EMAIL_POST_LINKS2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Whether to show a link for users to e-mail postsfalseMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_FEED_REDIRECT_URL2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00URL to redirect post feed requests toMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_FLOAT_ALIGNMENT2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Whether float alignment is enabled for the blogtrueMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_LOCALE2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Language for this blogenMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_M2B_WHITELIST_EMAIL2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00List of email addresses that can post to the blog via email.Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_MAX_NUM2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Maximum number of things to show on the main page"7Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_MAX_UNIT2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Unit of things to show on the main pagePOSTSMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_META_DESCRIPTION2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00The meta description of the blog served to search engines.A look into the mind and methods of an aspiring gamedev.Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_META_DESCRIPTION_ENABLED2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Whether this blog is served with meta descriptions.trueMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_NAME2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00The name of the blogAdventures of An Amateur GameDevMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_PER_POST_FEED2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00The type of feed to provide for per-post commentsFULLMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_POST_FEED2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00The type of feed to provide for blog postsFULLMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_POST_FEED_FOOTER2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Footer to append to the end of each entry in the post feedMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_POST_TEMPLATE2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00The template for blog postsMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_PROMOTED2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Whether this blog can be promoted on BloggertrueMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_QUICK_EDITING2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Whether Quick Editing is enabledtrueMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_READ_ACCESS_MODE2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00The access type for the readers of the blog.PUBLICMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_READER_PERMISSION2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00The list of emails for users who have permission to read the blog.Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_SEARCHABLE2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Whether this blog should be indexed by search enginestrueMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_SEND_EMAIL2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Comma separated list of emails to send new blog posts toMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_SHOW_TITLE2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Whether to show the title fieldtrueMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_SHOW_URL2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Whether to show a related link box in the post composerfalseMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_SUBDOMAIN2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00The BlogSpot subdomain under which to publish your blogaaagdMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_TIME_STAMP_FORMAT2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00The number of the time stamp format27Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_TIME_ZONE2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00The time zone for this blogAmerica/New_YorkMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.settings.BLOG_USE_LIGHTBOX2013-11-30T10:03:47.693-05:002015-01-24T20:21:37.696-05:00Whether to show images in the Lightbox when clickedtrueMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.comtag:blogger.com,1999:blog-3223852017784708240.post-514203991706252742014-01-27T10:54:00.001-05:002014-01-27T10:54:43.481-05:00Candy Task Force: Candy Jam Submission<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJPWcq593ljf2aZWzOrCd9JihJ1jmLs6RiFZ2bGA2Da3dMWV9nJgcJ2RGSWTxyz7WgCWnh1yrHhqBlUeaUF_7iToZyd2CL-XcNgtyHk3VEETHPA0QnnSk-1-eGFMdUAdhB6kWLM0YI5tM/s1600/CTF+Cover.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJPWcq593ljf2aZWzOrCd9JihJ1jmLs6RiFZ2bGA2Da3dMWV9nJgcJ2RGSWTxyz7WgCWnh1yrHhqBlUeaUF_7iToZyd2CL-XcNgtyHk3VEETHPA0QnnSk-1-eGFMdUAdhB6kWLM0YI5tM/s1600/CTF+Cover.png" /></a></div><br />Presenting my submission to the <a href="http://itch.io/jam/candyjam" target="_blank">Candy Jam</a>, and my first ever released game <a href="http://mateodon.itch.io/candy-task-force" target="_blank">Candy Task Force</a>. &nbsp;CTF is a 60 second shooter where the player must distinguish between knockoffs and original apps to score. &nbsp;The premise is that you are a lawyer filling copyright claims against clones of your employer's candy based match-3 game. &nbsp;The mechanics are simple, shooting knockoffs increases your score and shooting originals causes a counterclaim, get 3 counterclaims and it's game over.<br /><br />Given my self imposed time constraint I think I did a good job illustrating a perfect system, where companies cannot attack anyone with impunity just because they can afford it. &nbsp;In the end that's what I take issue with, the flaws of the current system that allow common words and phrases to be trademarked and allow those words to be used as a weapon to target smaller companies merely to establish a record of victories (yeah, they're basically just hacking their win-ratio).<br /><br />So in the end I decided to pull many of the pot-shots I took at King and I think it's a better game for it. &nbsp;Don't get me wrong I still think they're sleazy to take advantage of the system in this way, but the fact is there is one flawed copyright system, yet there are countless sleazy companies that would take advantage of it. &nbsp;Cutting the head off the hydra would just create two more in it's place.<br /><br />Speaking of sleazy companies, I think I also did a good job addressing the other issue at hand here, cloning games. &nbsp;It's a serious problem, and like I said in the last post I understand that King has a very valid right to take action against the obvious knockoffs (many of which have millions of downloads). &nbsp;Another Hydra can be found here, shutting down one knockoff just makes room for more knockoffs (hence the respawing apps in my game). &nbsp;The distributors are the root here, and too few take responsibility for the content they distribute. <br /><br />All this in a 60 second game? &nbsp;Well technically it's just a glorified target range, but don't tell anyone, I don't have a lawyer to protect me from Target Corporation so I'd have to take it down :PMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-46592670175575636312014-01-25T09:48:00.002-05:002014-01-25T09:48:55.749-05:00Candy JamI'll be taking a short break from Project Draconyx to participate in the&nbsp;<a href="http://itch.io/jam/candyjam" target="_blank">Candy Jam</a>, and while the deadline for this game jam is Feb 3rd, I've decided set a 48 hour limit for my submission, by Monday I'll be back to work on Draconyx.<br /><br />The Candy Jam has stemmed from <a href="http://www.rockpapershotgun.com/2014/01/21/stealing-candy-from-babies-king-embrace-the-aristocracy/" target="_blank">King.com's copyright enforcement</a> of the word "Candy" in relation to it's hit time killer Candy Crush Saga. &nbsp;On the surface this looks like a cut and dry case of absurdity, but I actually support King in it's pursuit of trademark infringement, a quick search of google's app store shows that there are dozens of clones of this popular mobile title, and those people deserve a steamy dose of legal heat for their blatant ripoffs. <br /><br />Inevitably there would be some collateral damage from such an offensive, and the title (mentioned in the above article) "All Candy Casino Slots" is a fair example of an original (forgive the term original, casinos and slots are certainly not a new concept) title fallen under King's legal crosshairs due to only marginal similarities. &nbsp;I'm not OK with this, but like I said, it's collateral damage in a necessary war. &nbsp;It's a shame and I wish the developer the best of luck appealing it, but that alone wouldn't provoke me to take up arms.<br /><br />What prompted me to act was the discovery that <a href="http://kotaku.com/why-the-ridiculous-candy-crush-vs-banner-saga-conflict-1507064357" target="_blank">King is also enforcing copyrights on the word "Saga"</a>. &nbsp;This time the line is clear, Banner Saga is certainly not a clone of Candy Crush, it was targeted merely for containing the word Saga. &nbsp;This type of legal bullying cannot continue, if this were the norm any developer with a legal team could push around indies with impunity. &nbsp;I don't want to have to worry that any word in the (as yet undecided) title of my project is copyrighted and that the holder of that copyright can attack me regardless of product similarity.<br /><br />So my submission will attempt to highlight those beliefs, that a company can and should protect it's property from blatant imitators, but attacking anything with a similarity is disgraceful and unjust. &nbsp;Hopefully I can get that point across in 48 hours.Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-52474355460319090602014-01-24T09:15:00.000-05:002014-01-24T09:15:05.335-05:00Project Draconyx: Miscellaneous FeaturesThere are still some small things this project needs before I can focus on level design, so this step is dedicated to filling in those last pieces of the puzzle (or, I guess this process has been more like making the puzzle pieces, the beta design process would better equate to putting the puzzle together). &nbsp;The following list may change as I think of other last-minute details.<br /><br /><br /><ul><li>Pickups</li><li>Loot Drops</li><li>Potion Use</li><li>Switch/Hotkey Weapons</li><li>Soundscape</li><li>Portal (random level) Logic</li></ul>Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-43504092463572162172014-01-22T12:13:00.000-05:002014-01-22T12:13:15.261-05:00Project Draconyx: Inventory<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqkDLlpDdZ-jHI8PbGKHbBOlKzjjDHWIhm74rm-_lqCCRxD6d6SDGA7h0dsUC5PL6ELAWrFZYpecv13L74YUMycXUg5lyo4BuYdeQf7zBzGxkCl3wPWiylZ4cpJoMsmUVgq3w3UkAl4CI/s1600/Inventory.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqkDLlpDdZ-jHI8PbGKHbBOlKzjjDHWIhm74rm-_lqCCRxD6d6SDGA7h0dsUC5PL6ELAWrFZYpecv13L74YUMycXUg5lyo4BuYdeQf7zBzGxkCl3wPWiylZ4cpJoMsmUVgq3w3UkAl4CI/s1600/Inventory.png" height="362" width="640" /></a></div><br />I decided on a very basic inventory system, to equip an item simply drag it from the it's slot in the storage panel (upper) to a slot in the equipped panel (lower). &nbsp;Weapon slots are blue and shield slots are purple, I know this conflicts with the traditional RPG rarity color-code, but I don't plan on displaying rarity this way and it looks nicer without a jumble of labels floating around to tell you which item goes where. At a later date I may change this slot system to use different shapes or patterns instead, but for the time being I'm satisfied with the result. &nbsp;Clicking on an item will display it's details in the info panel. <br /><br />The downside to my inventory solution is the lack of automated or manual organization. &nbsp;The empty space in the above screenshot is actually occupied by items I have not added to the player yet, if the player does not have an item it is hidden in the inventory (the empty blue slot in the storage panel belongs to the equipped mace). &nbsp;As the player collects items the blanks fill in, but they cannot manually rearrange the items, nor does the system consolidate the empty spaces. &nbsp;This is something I would like to add eventually however I don't believe it's integral to the game, and think it's time to move forward to other areas.<br /><br />The equip-able items use NGUI's DragDrop scripts, when an item is dropped on an equip slot it performs logic to unequip any old items in the slot and equip the new one. &nbsp;I'll need to modify the UFPS switch weapon and hotkey functions to only access the weapons in the equipped panel, but I'm trying to postpone major source code changes until the very end (in case of updates) so for now I made a UFPS TryEquip node for uScript, so the player equips the first weapon in the equipped panel when the inventory closes. &nbsp;Shields are fully functional though as the only way to equip a shield is to open the inventory and drag it to the shield slot in the equipped panel.<br /><br />The ammo and potion counters use similar logic to the <a href="http://aaagd.blogspot.com/2014/01/project-draconyx-player-hud-part-6.html">potion counter</a>&nbsp;on the HUD to display quantities. &nbsp;These items aren't equip-able and therefor don't have DragDrop scripts attached, the health potion is on the equipped panel because it will be sort of a "quick-use" item.Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-9438391217327925922014-01-16T10:01:00.002-05:002014-01-16T10:01:42.973-05:00Project Draconyx: Leveling and Skills<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2UxUF6Y71l1CqsZYQJIwWvlmELvvTro3o5oJY6tLYPTAzUqdtBrr93wkkiRMEy-hC6vYHgqdzzZUuyqHS5L3hmgeg7BuwpcyewPcfeV_9dktP_c3-51Nr1p8dVZM0jQAkfPnFvKZtyVU/s1600/1skills.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg2UxUF6Y71l1CqsZYQJIwWvlmELvvTro3o5oJY6tLYPTAzUqdtBrr93wkkiRMEy-hC6vYHgqdzzZUuyqHS5L3hmgeg7BuwpcyewPcfeV_9dktP_c3-51Nr1p8dVZM0jQAkfPnFvKZtyVU/s1600/1skills.png" height="392" width="640" /></a></div><br />The skill system in Project Draconyx is somewhat unorthodox, rather than xp and skill trees I use gold and randomized cards. &nbsp;The player is presented with between one and three skill cards, when they purchase one of the skills player level is increased and new cards are dealt.<br /><br />The system is complex, but can be broken down into 3 parts, the card dealer, the card interactions, and the level up.<br /><br />The card dealer randomly picks 3 cards from a list, it performs numerous checks to make sure we have 3 new and different cards (ex: in the above screenshot, if we buy a skill we know that the Warrior, Healer, and Leather Armor cards will not be in the next hand, and we also know that the hand will never contain duplicate cards). &nbsp;There are 2 blanks in the list, which don't count as duplicates, so the player could potentially get only a single choice.<br /><br />Card interactions are controlled by a number of NGUI actions and events. &nbsp;When a card is clicked it checks if the player has enough gold to buy the skill and if so it unlocks the button, it also sends details on the skill to the info panel. &nbsp;When the button is clicked (after it is unlocked) it signals the level up.<br /><br />The level up subtracts the value from the player's gold, adds 1 to the player's level, the cost of the card is increased by 25%, and the bonus is applied to the player stats. &nbsp;After all that it broadcasts the updatePlayerStats message, and new cards are dealt.Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-61790335680292896792014-01-13T12:06:00.002-05:002014-01-13T17:57:24.495-05:00Project Draconyx: Player Skills and Inventory part 1, Preparation.<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicUWz7wWfDR55dP0l_6KjV6w3NakGdQDxrrlAO7w16dVOV1UchqqAGExv9Y20N1VjDWz9cWQH2z-yD3uX_0vtUva-LhpBQXsfQZiJYqDNc5F2rggjkyn_9tUW4JbOH3TdIKUlaJS6hV-o/s1600/1menu.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="356" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEicUWz7wWfDR55dP0l_6KjV6w3NakGdQDxrrlAO7w16dVOV1UchqqAGExv9Y20N1VjDWz9cWQH2z-yD3uX_0vtUva-LhpBQXsfQZiJYqDNc5F2rggjkyn_9tUW4JbOH3TdIKUlaJS6hV-o/s640/1menu.png" width="640" /></a></div><br />To prepare for skills and inventory I've made a new NGUI panel for the menus and added the&nbsp;<a href="http://www.tasharen.com/ngui/NGUIuScript.cs" target="_blank">NGUI uScript events</a>&nbsp;by Tasharen, I've also made a UFPS pause node so we can stop gameplay and unlock the cursor while in the menus.<br /><br />The buttons and stat panel are functional (technically the info panel is too, but we don't have any info for it yet). &nbsp;The menu panel has a script to open and close the menus, the buttons each have a script to send open and close signals to the menu panel. &nbsp;The stat panel just listens for the updatePlayerStats event to refresh it's display.<br /><br />Next I will add the skills menu and then inventory.Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-722629033215374792014-01-11T07:24:00.000-05:002014-01-11T07:24:44.346-05:00Project Draconyx: Player HUD part 8, Message Log<div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-cUqRfNMYiFMu9vavRd10t-p4OtJsT4BWSM555JC3WYRY0RMPWikNZ7MivCMltd7AIoUm6pWws_WMBLm4rhJdk0UQhmCM1-AdrGXHFRM5op5Gs8wpQZ0EqLcGuXgmnlOH0qmtyTRD10c/s1600/1messagelog.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi-cUqRfNMYiFMu9vavRd10t-p4OtJsT4BWSM555JC3WYRY0RMPWikNZ7MivCMltd7AIoUm6pWws_WMBLm4rhJdk0UQhmCM1-AdrGXHFRM5op5Gs8wpQZ0EqLcGuXgmnlOH0qmtyTRD10c/s1600/1messagelog.png" height="362" width="640" /></a></div><br />The message log is composed of an NGUI panel with soft clip, 5 labels, and 5 empty widgets which are used for the message positions. &nbsp;I had initially built the log with an NGUI table, however I had difficulty getting the text positioned properly with soft clip (fade at the top of the log). &nbsp;The soft clip panel actually extends below the visible screen so we don't fade the bottom messages, and because the table was parented to this panel it would push messages off screen when it ran out of room.<br /><br />Again, my solution was to fake it, I just use the same 5 labels and update the text of the oldest one when a new message comes in. &nbsp;The new message is moved to the bottom position and the old messages are pushed up.<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiI8auRVfuVCx9AEgqV2JJGQS_rh2RzqML6a05SDCus-Uky2XDTRWP5SnbBl7seQLCBWppdrqSyfdc4rJOLHSR_ghFnzvDmM7iJrXHfoEP2EIl3nukCvC9H1CzQGef2MXW0JHNSNuY0R2Q/s1600/2messagelogic1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiI8auRVfuVCx9AEgqV2JJGQS_rh2RzqML6a05SDCus-Uky2XDTRWP5SnbBl7seQLCBWppdrqSyfdc4rJOLHSR_ghFnzvDmM7iJrXHfoEP2EIl3nukCvC9H1CzQGef2MXW0JHNSNuY0R2Q/s1600/2messagelogic1.png" height="260" width="640" /></a></div><br />Receiving the message is a matter of listening for the HUDMessage event, my <a href="http://aaagd.blogspot.com/2014/01/project-draconyx-player-hud-planning.html">HUDHelper</a> will send UFPS messages through this, but I can send additional messages through uScript.<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgl-NzlBHBhPfHumoMpMlJq-fdhBK8olMrEzI8oitWxjchHHqGZ10YyBqv4cIm_4bdUVN_0C3zOexUSjG6Y31PwAxn2a0BTxni-wo-MjDPXSW8umO74XcANlfAilr6DleFO2Lk_U3ZS2jg/s1600/2messagelogic2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgl-NzlBHBhPfHumoMpMlJq-fdhBK8olMrEzI8oitWxjchHHqGZ10YyBqv4cIm_4bdUVN_0C3zOexUSjG6Y31PwAxn2a0BTxni-wo-MjDPXSW8umO74XcANlfAilr6DleFO2Lk_U3ZS2jg/s1600/2messagelogic2.png" height="150" width="640" /></a></div><br />A switch runs the logic to push messages up, the image above is one of 5 logic chains the switch can run, the other 4 are nearly identical to this, I just move the message objects to the left each time.Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-18882972994765956202014-01-10T10:39:00.002-05:002014-01-10T10:39:07.401-05:00yesdraconyx enemy wishlist<a href="https://www.assetstore.unity3d.com/#/content/12082" target="_blank">troll golem</a>&nbsp;$35 possibly mecanim only<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/11201" target="_blank">the troll</a>&nbsp;$15 possibly mecanim only<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/13626" target="_blank">necromancer</a>&nbsp;$10 possibly mecanim only<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/8208" target="_blank">boss demon</a>&nbsp;$15<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/7521" target="_blank">minotaur warrior</a>&nbsp;$20 possibly mecanim only<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/5597" target="_blank">demon girl</a>&nbsp;$10<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/13679" target="_blank">vampire bat</a>&nbsp;$10<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/11045" target="_blank">mosquito monster</a>&nbsp;$5<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/2176" target="_blank">insectiod</a>&nbsp;$15<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/9004" target="_blank">alien arachnid</a>&nbsp;$15<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/8224" target="_blank">deamon bloody queen</a>&nbsp;$15 (not great, but so few females...)<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/452" target="_blank">monster zombie</a>&nbsp;$55 (expensive, not many animations, but looks creepy)<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/3138" target="_blank">total horror</a>&nbsp;$30 (silly walk anim, but looks scary)<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/13013" target="_blank">undead king</a>&nbsp;$65 (expensive, mecanim only, no anims, but looks bad ass)<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/281" target="_blank">human undead</a>&nbsp;$25 (would need to retex denim, but looks gory)<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/2325" target="_blank">mummy</a>&nbsp;$50<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/4362" target="_blank">dinosaur pack</a>&nbsp;$30 (lol dinos, but hey $30 for a bunch of big scary beasts is a good deal)<br /><br />flying teapot is working on a creature pack (due in feb?) <a href="http://forum.unity3d.com/threads/130252-Do-you-need-Creature-pack/page3" target="_blank">thread</a><br /><br /><b>3dfoin</b><br /><a href="https://www.assetstore.unity3d.com/#/content/4560" target="_blank">goblin assassin</a>&nbsp;$15<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/4567" target="_blank">short goblin</a>&nbsp;$15<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/13980" target="_blank">soul eater</a>&nbsp;$10<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/4531" target="_blank">basilisk</a>&nbsp;$15<br /><br />mr. necturus<br /><a href="https://www.assetstore.unity3d.com/#/content/6516" target="_blank">ratkin slave</a>&nbsp;$15<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/7110" target="_blank">army of skeletons</a>&nbsp;$45<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/3791" target="_blank">warlock</a>&nbsp;$25<br /><br /><b>arbuznikov </b>(make offer on pack at <a href="http://www.cgtrader.com/3d-models/character-people/fantasy/alien-bugs-pack" target="_blank">cgtrader</a>)<br /><a href="https://www.assetstore.unity3d.com/#/content/11457" target="_blank">alien bug 103</a>&nbsp;$25<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/11519" target="_blank">alien bug 203</a>&nbsp;$25<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/11447" target="_blank">alien bug 102</a>&nbsp;$25<br /><br /><b>protofactor&nbsp;</b>(make offer at&nbsp;<a href="http://www.cgtrader.com/3d-models?utf8=%E2%9C%93&amp;price%5Bfrom%5D=0&amp;price%5Bto%5D=20000&amp;author=protofactor&amp;keywords=&amp;sort_by=highest_price&amp;per_page=50" target="_blank">cgtrader</a>)<br /><a href="https://www.assetstore.unity3d.com/#/content/4041" target="_blank">chimera</a>&nbsp;$35<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/514" target="_blank">demon</a>&nbsp;$30<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/4530" target="_blank">dragonide</a>&nbsp;$30<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/573" target="_blank">protoxoloth</a>&nbsp;$20<br /><br /><b>dexsoft</b><br /><a href="https://www.assetstore.unity3d.com/#/content/12803" target="_blank">viking</a><b>&nbsp;</b>$40<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/12805" target="_blank">black knightress</a>&nbsp;$40<br /><br /><a href="https://www.assetstore.unity3d.com/#/content/2135" target="_blank">barbarian</a>&nbsp;$40<br /><br /><b>code this lab </b>(<a href="http://www.codethislab.com/" target="_blank">website</a> has 9 zombies for $400, also can make offers on website)<br /><br />zombie; female, farmer, male, butcher, maid, little girl, little boy. $50 each<br /><br />monster; skinless crawler, leather ghost, genetic, giant tentacular. $50 each<br /><br /><br /><br />Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-42988596021851172632014-01-10T10:38:00.000-05:002014-01-10T10:39:55.080-05:00Project Draconyx: Player HUD part 7, Interaction Prompt<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnzX0NEwBr5WMS0wmIiBnk-ERbCu8rqj_vF4CYREDEJu0Cn0eY6eWn6xNpVXMiMbbQc2f16phP_Zf_vIXAPxhB2wtmq6Oj4uqgfytX1jureM4sJxU2epmABbnJ__Bpu2DYwUT-zrakg3U/s1600/1prompt.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjnzX0NEwBr5WMS0wmIiBnk-ERbCu8rqj_vF4CYREDEJu0Cn0eY6eWn6xNpVXMiMbbQc2f16phP_Zf_vIXAPxhB2wtmq6Oj4uqgfytX1jureM4sJxU2epmABbnJ__Bpu2DYwUT-zrakg3U/s1600/1prompt.png" height="302" width="640" /></a></div><br />The interaction prompt is an NGUI label that is visible when the player is in range of an interactable. &nbsp;The logic is straight forward, we already have an interaction event from the&nbsp;<a href="http://aaagd.blogspot.com/2013/12/project-draconyx-interaction-manager.html">interaction manager</a>, but I went back to that graph to send a noInteract signal when it clears the text.<br /><br />To show or hide the interaction prompt we just listen for the event/signal, update the label's text, and fade in/out it's alpha.<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjm9qzR0QfygrVc49V_KeaWUo1VuYOikTfvxDmTH_eqfSppWWfYDu5abjTL9DLvwVAmKjOl6pWLuSjgbaF9STVerl812IPi2zPdpYxg2sl_CXnmFNDRqJUTQqDlv-rtqJGYcJLrj3hzo9I/s1600/2promptlogic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjm9qzR0QfygrVc49V_KeaWUo1VuYOikTfvxDmTH_eqfSppWWfYDu5abjTL9DLvwVAmKjOl6pWLuSjgbaF9STVerl812IPi2zPdpYxg2sl_CXnmFNDRqJUTQqDlv-rtqJGYcJLrj3hzo9I/s1600/2promptlogic.png" height="366" width="640" /></a></div><br />Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-59851578222343000342014-01-09T17:02:00.001-05:002014-01-22T12:04:08.125-05:00Project Draconyx: Player HUD part 6, Health Potion Counter<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcapjt7QJ7gThxsbZ00_j9DQ6BKF89SBJEdx7Jito-rtC5LbnKZX_7tAxBLkShhTMRBaGs4YOXjsuHdijrYtTeBvqB_lW5itI9CkzlcjGluthScYk1MgBEwMIYUsmi7AOsE1zJCRw4ZIA/s1600/1healthpotioncounter.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjcapjt7QJ7gThxsbZ00_j9DQ6BKF89SBJEdx7Jito-rtC5LbnKZX_7tAxBLkShhTMRBaGs4YOXjsuHdijrYtTeBvqB_lW5itI9CkzlcjGluthScYk1MgBEwMIYUsmi7AOsE1zJCRw4ZIA/s1600/1healthpotioncounter.png" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Like the ammo counter the health potion counter uses an icon and a label, note that I don't have health potion functionality yet, that will probably be implemented after the inventory, to test the counter I made a gun use health potions as it's clip.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">The health potion counter logic checks the quantity of health potions in UFPS's SimpleInventory, and updates the counter when it detects a change in value. &nbsp;When the count is zero it hides the display (icon and label).</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHXUfhep1pADrE-D8cQw4d-6YTlLk4vdkypKLT-W0XrjsvN6YJRhWqT7cDqfZl1Nb9Zix9qKaKFWiw2AXwZGAPVvz0FEqBELn2ka27hSNZCrnPA6QeVOxKlmAbfhPP8ImCCBKuUId8iq4/s1600/2potioncountlogic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgHXUfhep1pADrE-D8cQw4d-6YTlLk4vdkypKLT-W0XrjsvN6YJRhWqT7cDqfZl1Nb9Zix9qKaKFWiw2AXwZGAPVvz0FEqBELn2ka27hSNZCrnPA6QeVOxKlmAbfhPP8ImCCBKuUId8iq4/s1600/2potioncountlogic.png" height="232" width="640" /></a></div>Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-75051688597081804742014-01-09T10:23:00.000-05:002014-01-09T17:04:36.971-05:00Project Draconyx: Player HUD part 5, Ammo Counter<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_S0YnUMZBpSPE3dCVttzGwHpvXDbeKxMFLfpB_FZZsWtKznk5KVaS7dEJcGyGa22LXcAdlP0eRdQ89m-VTE7cGFqDdw65g8WBR8SOY4T3CBy2NIwJLZYE_WUorwsg3JSq7d5YLzfWrA0/s1600/1ammocounter.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_S0YnUMZBpSPE3dCVttzGwHpvXDbeKxMFLfpB_FZZsWtKznk5KVaS7dEJcGyGa22LXcAdlP0eRdQ89m-VTE7cGFqDdw65g8WBR8SOY4T3CBy2NIwJLZYE_WUorwsg3JSq7d5YLzfWrA0/s1600/1ammocounter.png" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">The ammo counter is composed of an icon and an NGUI label. &nbsp;The icon has it's own graph to check the clip type that the equipped weapon is using, this will hide the icon when a weapon is using a different type of ammo, or none in the case of melee weapons (I am still using the default AmmoClip, but when I add more ammo types I can use a different icon for each one).</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwt6rja8RfuA82aQWW5r48ol9qlFz3dzwPpc2e5x2PyulgcKhqVxn6gQUaEnSZi4b8gtlPeIiI__8Q300eNtG-1pEY2_Y4dhVQSALlKRnXTdMs8swiG6oHc8guSP0fLLKC9k9o3b4JoTM/s1600/2iconlogic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwt6rja8RfuA82aQWW5r48ol9qlFz3dzwPpc2e5x2PyulgcKhqVxn6gQUaEnSZi4b8gtlPeIiI__8Q300eNtG-1pEY2_Y4dhVQSALlKRnXTdMs8swiG6oHc8guSP0fLLKC9k9o3b4JoTM/s1600/2iconlogic.png" height="288" width="640" /></a></div><br />The label uses similar logic to hide the ammo count when a melee weapon is equipped. &nbsp;However the actual ammo counting logic is more complex than I had hoped it to be because UFPS's FPWeaponReloader script subtracts from the clip count at the start of the reload animation, and adds to the ammo count at the end. &nbsp;Simply adding ammo + clips to get total ammo did not work as expected, after firing (during auto reloading) the counter would display 10, 9, 8, 9 (when it should just go from 10 to 9). &nbsp;My solution basically fakes it by ignoring the ammo count and just adding 1 to the clip count, unless the clip count is zero, then it does use the ammo count. &nbsp;I'll show a less convoluted example of a counter in the next step.<br /><br />I've also made another UFPS node for uScript to get the clip type of the equipped weapon.<br /><br /><br />Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-39461566780757267662014-01-08T16:17:00.000-05:002014-01-08T16:17:21.448-05:00Project Draconyx: Player HUD part 4, Health and Stamina Meters<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtCvM1IMXP-3-_399JD02vrnF6kERf3TSxC0ci_fILZj0ieiUJU43Ed_PP5vMNmffUDI0nY5Da7OzQ-xPIZiIAj0VVr2-bgzUx1GNF0lJiuoDwe9W6jfGksD-f_2sQtmeHyiPXJU9aD8I/s1600/1meters.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtCvM1IMXP-3-_399JD02vrnF6kERf3TSxC0ci_fILZj0ieiUJU43Ed_PP5vMNmffUDI0nY5Da7OzQ-xPIZiIAj0VVr2-bgzUx1GNF0lJiuoDwe9W6jfGksD-f_2sQtmeHyiPXJU9aD8I/s1600/1meters.png" height="302" width="640" /></a></div><br />The health and stamina meters are each made up of an image and an NGUI panel. &nbsp;To make the meters shrink when health/stamina is depleted I enabled soft clipping on the panels so that when the images are rotated only a portion of them are visible.<br /><br />The only difference between health and stamina is the direction of rotation and the variables they get their data from. &nbsp;The health meter uses the currentHealth logic from earlier, and the stamina uses identical logic to get a currentStamina value.<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-_ssUnkMUmV2dbiVwVQAHdh-yUK71a-d2m3jYQqMyCm5cvh86ywDbptYu7okRZPzY7rwx5NAFOgTbJdNstjXt6_7JCDWytGN2_ozRVbqnS3k_KlcgSYrSXtH3TWsPj0TTBM2XpQzSs0I/s1600/2healthmeterlogic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj-_ssUnkMUmV2dbiVwVQAHdh-yUK71a-d2m3jYQqMyCm5cvh86ywDbptYu7okRZPzY7rwx5NAFOgTbJdNstjXt6_7JCDWytGN2_ozRVbqnS3k_KlcgSYrSXtH3TWsPj0TTBM2XpQzSs0I/s1600/2healthmeterlogic.png" height="211" width="640" /></a></div><br />The current health/stamina logic activates lerp functions which determine the rotation of the meters (0 = full health/stamina, 180/-180 = no health/stamina).Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-12544977137584962052014-01-07T13:56:00.000-05:002014-01-07T13:56:23.563-05:00Project Draconyx: Player HUD part 3, Compass<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiL5L5S6UqRZWgDcTMWh2lh6ZhbnBUI_bRD2CjthPD4KpOCfulkrFIVKsj7wnQW6AX_uZYIGK04u3IOzZUVdzV2hOyi3qa2z8A8cxTWFL6fmjLDvQL2sTQj6SOqnXNca8UT6fGk0BaT3YQ/s1600/1compass.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiL5L5S6UqRZWgDcTMWh2lh6ZhbnBUI_bRD2CjthPD4KpOCfulkrFIVKsj7wnQW6AX_uZYIGK04u3IOzZUVdzV2hOyi3qa2z8A8cxTWFL6fmjLDvQL2sTQj6SOqnXNca8UT6fGk0BaT3YQ/s1600/1compass.png" height="198" width="200" /></a></div><br />To tell the truth I was hesitant about adding a compass because I <i>want</i> the player to feel lost, that's part of the excitement in randomized levels, even if you've played it before you still won't know where to go. &nbsp;However there is a difference between feeling lost and feeling disoriented, feeling lost can be exciting, feeling disoriented is just confusing and frustrating.<br /><br />I believe a compass is perfect for giving the player a sense of direction without holding their hand. &nbsp;There won't be any radars, waypoints, or floating arrows though, the player is still responsible for finding their own way.<br /><br />All we really need to make a compass is the player's rotation on the Y axis, but if we set the compass rotation directly to the player's we get a very stiff compass that doesn't feel natural.<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1r-YWW4YHa6UPZabmo9HmuA2IU1ZBYmWDbecRtAa0PVffu8xkBrBs4EO5XIA5m1T7-AfqPvFUiSSJzazEjn4a3-WvV0LyQgVJnoCxr68DVRth44ciuLOL4KWHW5SZEIv9-c6l-bRE41Q/s1600/2compasslogic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg1r-YWW4YHa6UPZabmo9HmuA2IU1ZBYmWDbecRtAa0PVffu8xkBrBs4EO5XIA5m1T7-AfqPvFUiSSJzazEjn4a3-WvV0LyQgVJnoCxr68DVRth44ciuLOL4KWHW5SZEIv9-c6l-bRE41Q/s1600/2compasslogic.png" height="202" width="640" /></a></div><br />My solution has two parts, first there is a dead zone (+/-8 degrees) so the compass doesn't respond to every little twitch the player makes, then I interpolate the compass turn speed to slow down the compass rotation as it nears it's destination. &nbsp;The result is a compass which behaves much more naturally while still being accurate and responsive.Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-61385041730539298322014-01-06T21:21:00.001-05:002014-01-06T21:21:33.368-05:00Project Draconyx: Player HUD part 2, Near Death Filter<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRXAvHqPMxewQQChT9FBFiHEIe9jYYU6QqbVKjomOFZeOAgU6PGZcvdlbPfGS92kTArgcjhFu1Ah6Mnx647Sfvyu6xBvdcDGEbjqB9m9fRwBUNPpnUny1uHNEiXmtrUcKhi33uG3FPNfA/s1600/1neardeath.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjRXAvHqPMxewQQChT9FBFiHEIe9jYYU6QqbVKjomOFZeOAgU6PGZcvdlbPfGS92kTArgcjhFu1Ah6Mnx647Sfvyu6xBvdcDGEbjqB9m9fRwBUNPpnUny1uHNEiXmtrUcKhi33uG3FPNfA/s1600/1neardeath.png" height="302" width="640" /></a></div><br /><div class="separator" style="clear: both; text-align: center;"></div>The near death filter is composed of 2 images, a dark screen filter, and a red bloodshot effect. &nbsp;The veins on the bloodshot effect were made with <a href="http://kingskully.deviantart.com/art/Lightning-Brush-Pack-27167255" target="_blank">lightning brushes</a> by Kingskully.<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKMWBrKYpOEcfQ6rdewvjJCzTXpQNMKrS0kw-WlwnpQNgClB1t7-Z87otkgO9ZVkYAV4UXLy3k7bFiu2M5HTAOffg7gAdx4Z5pIA3kaQYZpCDmIwRz2dbN2NGGkAvEaWBIPZdNfBiWRhI/s1600/2scalehealth.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiKMWBrKYpOEcfQ6rdewvjJCzTXpQNMKrS0kw-WlwnpQNgClB1t7-Z87otkgO9ZVkYAV4UXLy3k7bFiu2M5HTAOffg7gAdx4Z5pIA3kaQYZpCDmIwRz2dbN2NGGkAvEaWBIPZdNfBiWRhI/s1600/2scalehealth.png" height="300" width="640" /></a></div><br />I added a new section to my HUD graph that checks the player's current health, and scales it to the health scale from the last step. &nbsp;This outputs to the near death logic whenever player health changes, later on I'll plug my health meter into this as well.<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi36Nmq2_9EEgsvv0htuCXKsa4vmdw9l3ydxXWJfDxm7xeAz-HOXB3SZFCRJL6kUCs5CdXRK4N1dfiVyksGup82qDzfuzg4E7Aj_VGpvVRo76TE0pGvOPiPzlhJrCG37J-BL9oi-9Dzb7Y/s1600/3neardeathlogic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi36Nmq2_9EEgsvv0htuCXKsa4vmdw9l3ydxXWJfDxm7xeAz-HOXB3SZFCRJL6kUCs5CdXRK4N1dfiVyksGup82qDzfuzg4E7Aj_VGpvVRo76TE0pGvOPiPzlhJrCG37J-BL9oi-9Dzb7Y/s1600/3neardeathlogic.png" height="328" width="640" /></a></div><br />The near death filter begins when current health is less than 0.2 (20%) and fades out if health rises above that value. &nbsp;The current health determines how strong the effects are, with 20% health it's barely noticeable, but by 1% the screen is very dark and the veins are brighter (the screenshot in this blog was taken at about 10% health).Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-87002467780171404602014-01-05T19:16:00.000-05:002014-01-05T19:19:22.314-05:00Project Draconyx: Player HUD part 1, Damage Flash<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVbHHOpw3DvdX6pAoJ4Nl_4aT8vLznHvH2izM0DKKckEAHT3rH8T2eu-zjPaEI38ckFohlzhlQKW2LWWZ8c3w96rTguSc8sgyV64kYL5zRTRnqJpcxc6UVkidm13ME0j_qwLkkHFD6uWg/s1600/1damageflash.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhVbHHOpw3DvdX6pAoJ4Nl_4aT8vLznHvH2izM0DKKckEAHT3rH8T2eu-zjPaEI38ckFohlzhlQKW2LWWZ8c3w96rTguSc8sgyV64kYL5zRTRnqJpcxc6UVkidm13ME0j_qwLkkHFD6uWg/s1600/1damageflash.png" height="302" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">The damage flash is composed of 3 images, the flash (red glow), and 2 blood splatters, light and heavy. &nbsp;I made the effects with GIMP, and the <a href="http://www.deviantart.com/art/Splatter-Brushes-45042105" target="_blank">splatter brushes</a> were provided by ka05.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">I will use one graph to control all aspects of the HUD, due to this it will end up being very large so I will post images of each section instead of the entire graph.</div><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjU1J-YdOki9YSeuR0e25sGYbilMJAZOEiLcyRuO0BjxkftcGv1V1drJ2ubwh-5IGZnd73HvFEeKBMvISd3RSoILTY7kWJLf_TVO1kuyJF3vaaLSXyKz0Xvltrc0KBnPRjHOkp7kqwO3sk/s1600/2HUDupdate.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjU1J-YdOki9YSeuR0e25sGYbilMJAZOEiLcyRuO0BjxkftcGv1V1drJ2ubwh-5IGZnd73HvFEeKBMvISd3RSoILTY7kWJLf_TVO1kuyJF3vaaLSXyKz0Xvltrc0KBnPRjHOkp7kqwO3sk/s1600/2HUDupdate.png" height="295" width="400" /></a></div><br />The first section sets the health scale, which we will need for the damage flash and later for other HUD elements too, this is used to keep all health related values relative to the player's MaxHealth. &nbsp;It performs this task when the game starts and also when the player levels up, note that I do not have an updatePlayerStats event yet , but I added the receiver here so I won't have to come back to it when I get into leveling (as I will have to do with some of the graphs I made earlier).<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiI_3Rlczq7G_q0bcRHKN_YiJB9NXni2ecVG0eQz-Yea5RgVUBWTsrjTIAuB0WADpOrCBYlnhUU3DBR2M2aHzZ4_WGKPeoo6ugnOJDMilaTIPt6SGWMZFd1uyJZzw37YFH_23lmCMBU5Hg/s1600/3flashlogic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiI_3Rlczq7G_q0bcRHKN_YiJB9NXni2ecVG0eQz-Yea5RgVUBWTsrjTIAuB0WADpOrCBYlnhUU3DBR2M2aHzZ4_WGKPeoo6ugnOJDMilaTIPt6SGWMZFd1uyJZzw37YFH_23lmCMBU5Hg/s1600/3flashlogic.png" height="202" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Here we receive the HUDDamageFlash event from the HUD_Helper and apply the healthScale to intensity. &nbsp;I made a dummy sprite with the NGUI tween scripts attached to it so that uScript can reflect the tween functions, this dummy can be deleted once we have the nodes in the graph.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">The flash effect is handled similarly to UFPS's SimpleHUD where the intensity controls the alpha, however with a scaled intensity the effect is relative to MaxHealth. &nbsp;The splatter effects appear when the scaled intensity is above certain thresholds (0.1 for light and 0.25 for heavy), intensity also determines duration before the splats fade out.</div><br />Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-11261024987265228932014-01-04T12:04:00.000-05:002014-01-11T08:05:29.712-05:00Project Draconyx: Player HUD, Planning and Preperation<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiryOlpvurNqpaF5qsg1YRZV0vvjo-3L4GkBPvpAvizYM-TJOom4i3bNIgNmw4DvibVzsiIDtL_pEMRumww7Q8jApqS-08RborXKZ8UaPPS402YZpjRkNbiUAgY35W8jcCX9gTrYb4hU0U/s1600/HUD+conceptNoDamage.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiryOlpvurNqpaF5qsg1YRZV0vvjo-3L4GkBPvpAvizYM-TJOom4i3bNIgNmw4DvibVzsiIDtL_pEMRumww7Q8jApqS-08RborXKZ8UaPPS402YZpjRkNbiUAgY35W8jcCX9gTrYb4hU0U/s640/HUD+conceptNoDamage.png" height="360" width="640" /></a></div>I sketched out several HUD designs before deciding on this style, my choice is a design I feel is both informative and unintrusive. &nbsp;The actual HUD will likely be a smaller scale than the above concept, I wanted to clearly illustrate the design here. &nbsp;I should mention that the concept was created with&nbsp;<a href="http://www.gimp.org/" target="_blank">GIMP</a>, and the actual HUD elements will be too.<br /><br />The HUD will consist of several components, which will be divided into smaller segments in this blog.<br /><br /><ul><li>Damage Flash <a href="http://aaagd.blogspot.com/2014/01/project-draconyx-player-hud-part-1.html">done</a></li><li>Near Death Filter <a href="http://aaagd.blogspot.com/2014/01/project-draconyx-player-hud-part-2-near.html">done</a></li><li>Compass <a href="http://aaagd.blogspot.com/2014/01/project-draconyx-player-hud-part-3.html">done</a></li><li>Health and Stamina Meters <a href="http://aaagd.blogspot.com/2014/01/project-draconyx-player-hud-part-4.html">done</a></li><li>Ammo/Clip Counter <a href="http://aaagd.blogspot.com/2014/01/project-draconyx-player-hud-part-5-ammo.html">done</a></li><li>Health Potion Counter <a href="http://aaagd.blogspot.com/2014/01/project-draconyx-player-hud-part-6.html">done</a></li><li>Interaction Prompt <a href="http://aaagd.blogspot.com/2014/01/project-draconyx-player-hud-part-7.html">done</a></li><li>Message Log <a href="http://aaagd.blogspot.com/2014/01/project-draconyx-player-hud-part-8.html">done</a></li><li>Crosshair (see below)</li></ul><div><br /></div><div>To prepare for this step I removed the SimpleHUD component from the player and added an NGUI root and camera. &nbsp;UFPS's default HUD won't entirely go to waste though, I copied the OnMessage_HUDText and OnMessage_HUDDamageFlash methods over to a new script named HUD_Helper. &nbsp;On these methods I send custom events to my NGUI HUD which I can access in a uScript graph.<br /><br /><b>Crosshair</b><br />I originally intended to port UFPS's simple crosshair function over to my HUD so that I could use the interactive crosshairs. &nbsp;But I think that would be redundant, I already have interaction prompts and with the HUD Helper I can receive messages from UFPS, I can't think of a reason to include another notification system. &nbsp;So I've added a basic crosshair image to my HUD with no script, I'll probably add a crosshair toggle in the options later on.</div>Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-31596583249093870212014-01-03T10:00:00.000-05:002014-01-03T10:00:27.011-05:00Project Draconyx: Player Combat part 4, Melee Attacks<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggwEBP0DdvBYDagFprpyc7UjBYRwOfP6L7Amcs4o8MmatSD2Dz8ou1oSNCMBVRIqeAWT-3SuE1zFrtQlWqUsHkmC45t1p_FIYNJL_E-YPj6unnD0HskUdxxIqLKFDKFcsAfU8h0HB5dQc/s1600/1melee.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="360" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEggwEBP0DdvBYDagFprpyc7UjBYRwOfP6L7Amcs4o8MmatSD2Dz8ou1oSNCMBVRIqeAWT-3SuE1zFrtQlWqUsHkmC45t1p_FIYNJL_E-YPj6unnD0HskUdxxIqLKFDKFcsAfU8h0HB5dQc/s640/1melee.png" width="640" /></a></div><br />Again we start by making a new weapon from the skeleton sword and UFPS hands, melee states are a little more complex than ranged so I'll give an overview of my workflow for them. <br /><br />After adding the weapon to the player and inventory and setting the default position and rotation I add an attack state, to simplify the process I use a template with values copied from the UFPS mace states.<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpWKJZ9FNqVGOAbsjDfG4ztJtUbvdFOeLD4sGCLKLBsdjVyXLjOlbD0STLMpKKoIwyRKrjUF8FMZJa4vddQrXpwUVJEap5iF_86btu38TNanRiu0SGaHq3fVPHS-h9WuINHCHIJEbOYN8/s1600/2meleestate.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpWKJZ9FNqVGOAbsjDfG4ztJtUbvdFOeLD4sGCLKLBsdjVyXLjOlbD0STLMpKKoIwyRKrjUF8FMZJa4vddQrXpwUVJEap5iF_86btu38TNanRiu0SGaHq3fVPHS-h9WuINHCHIJEbOYN8/s640/2meleestate.png" width="372" /></a></div>I've simplified the naming convention, I get a little confused with the whole ToDown=PullUp and ToUp=PullDown concept (and even more so with the diagonal attacks) so I just make Attack1 trigger Pull1 and Swing1, Attack2 trigger Pull2 and Swing2, etc.. &nbsp;After creating a new attack state I uncomment one of the direction presets and add corresponding pull and swing states. &nbsp;Then I enter playmode and adjust the weapon position and rotation to get an idea of where my pull and swing states should be. &nbsp;And finally I test the attack and make adjustments, test and adjust again and again until I'm satisfied.<br /><br />I have made 5 attacks which can be used on swords of a similar size and speed, however when I add a larger variety of melee weapons I will have to make new states for many of them.<br /><br />I have also made a MeleeHelper graph to make melee swings consume stamina, as well as set the melee damage from another new modifier.<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLgRijcF_FdMe0QJNDG_7nkRTozNqLWCaoGtB24SQwRb5YCrxrnn4A15intCHpFEOMao4Asvl9Fb19CyF1Y3ZHrPTI6TNKATCz6-VhqC-ZOxvC96Oy25cBi7pL8zt0XDAhafxSNkHEmJ4/s1600/3meleehelper.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="358" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiLgRijcF_FdMe0QJNDG_7nkRTozNqLWCaoGtB24SQwRb5YCrxrnn4A15intCHpFEOMao4Asvl9Fb19CyF1Y3ZHrPTI6TNKATCz6-VhqC-ZOxvC96Oy25cBi7pL8zt0XDAhafxSNkHEmJ4/s640/3meleehelper.png" width="640" /></a></div>Note that the helper does not actually disable melee attacks when the player runs out of stamina, I considered making logic for that but I decided not to for the time being. &nbsp;My reason is because stamina and it's recharge rate are both very low so that the player can't continuously block or sprint away from enemies, both of which <i>do</i> require stamina. &nbsp;I think making the player unable to attack as well would undermine that forced-to-fight scenario, so currently the only penalty for spamming melee attacks is the inability to run or block.Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-77193807419398224682013-12-31T11:04:00.000-05:002013-12-31T11:18:27.749-05:00Project Draconyx: Player Combat part 3, Ranged Attacks<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJKJFjNnLLX_bAqBimGZol85YHAEORXohFtI-8bzz2ndOT014LNW6wot_uUlcFoNoAmqKoJM54IAkJRtmgJJl538ZlZ97NeJlnjOmcAJB-SkMbNI66CeIC-MduYOShqULQXC2GJdJqow0/s1600/1projectile.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="302" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJKJFjNnLLX_bAqBimGZol85YHAEORXohFtI-8bzz2ndOT014LNW6wot_uUlcFoNoAmqKoJM54IAkJRtmgJJl538ZlZ97NeJlnjOmcAJB-SkMbNI66CeIC-MduYOShqULQXC2GJdJqow0/s640/1projectile.png" width="640" /></a></div><br />First we need to make a new weapon, I'm using the staff from the Skeleton pack and UFPS hands. &nbsp;Setting the position and pivot as well as creating states for the weapon is a lengthy process of setting and testing values, the UFPS documentation covers this so I won't go into detail.<br /><br />The projectile is a copy of my&nbsp;<a href="http://aaagd.blogspot.com/2013/12/project-draconyx-ai-part-1-projectile.html" target="_blank">AI projectile</a>&nbsp;graph, with a new <a href="http://aaagd.blogspot.com/2013/12/project-draconyx-ai-part-4-stats-and.html" target="_blank">damage modifier</a>&nbsp;for player damage in the StatManager. &nbsp;However UFPS creates the projectile at the center of the screen rather than from a point on the weapon, the projectile is also created too close to the player so the colliders may intersect.<br /><br />To remedy these issues I made a ProjectileHelper graph which moves the projectile to an offset position, while keeping it aimed at the center of the screen. The helper also handles accuracy using the target's distance to scale projectile spread so that you are more likely to miss a distant target than a close one, again using another new modifier for accuracy (I will be manipulating these modifiers soon in the stats and leveling step).<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxbYg3OVzO0eqbuSgj1z1GCk8ybe2IyXt7qvJk6Gr_yURG5wz1E39wrGoHXpVNDPCkAJPKQMQj6nU7bmB9wT7472w_8ova-FOtjYCMnAgMK3C1Plkq-DsowQ8tJk-hDPYliv37PwunifQ/s1600/2ProjectileHelper.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgxbYg3OVzO0eqbuSgj1z1GCk8ybe2IyXt7qvJk6Gr_yURG5wz1E39wrGoHXpVNDPCkAJPKQMQj6nU7bmB9wT7472w_8ova-FOtjYCMnAgMK3C1Plkq-DsowQ8tJk-hDPYliv37PwunifQ/s640/2ProjectileHelper.png" width="640" /></a></div><br />The last issue to address is reloading, my staff is a single-shot weapon where each projectile fired consumes an entire "clip" (as UFPS refers to it, in-game I'll be calling it something like "mana"), which means after each shot the player must press "R" to manually reload. <br /><br />Up until now I've accessed UFPS in uScript entirely through reflection, however this time I could not get reflected actions for what I needed to do so I began making UFPS nodes for uScript. &nbsp;So far I have nodes to get current weapon ammo, get inventory item count, and reload weapon, I will likely make more and share them all at a later time.<br /><br />With the new nodes making an AutoReload graph was simple, it just checks the current ammo, checks the "clip" count, and performs a reload when necessary.<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7JgkYHFv1_L0ebk4liHKgUDJ6lLNrMKWeWnIkheT-0sYDDgj33vecHBuBAU7voyDBYxrKTnSRLahtDMl4JwixKN-R4h-RoAqRAZIBUowbOp6Pl_3AUB0JC3JrIq0-xnEtOl_tzp5ADMw/s1600/3AutoReload.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="208" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7JgkYHFv1_L0ebk4liHKgUDJ6lLNrMKWeWnIkheT-0sYDDgj33vecHBuBAU7voyDBYxrKTnSRLahtDMl4JwixKN-R4h-RoAqRAZIBUowbOp6Pl_3AUB0JC3JrIq0-xnEtOl_tzp5ADMw/s640/3AutoReload.png" width="640" /></a></div>Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-7213124842279846792013-12-27T10:24:00.001-05:002013-12-27T14:54:39.275-05:00Project Draconyx: Player Combat part 2, Blocking and Shields<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAGxDpfkW_aSEPGB5JUDJkHFYSURMrveit6MQcu1jYLWRDOpCsBF2zdrSf9BM9VvJta2RrtiwxS5cxeGaLixNuB6RBoCjlQCOQdnDu3GQGGj-qNoQ260h02R3EIwPo77WndDOKZ7XxVlA/s1600/1shield.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="302" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAGxDpfkW_aSEPGB5JUDJkHFYSURMrveit6MQcu1jYLWRDOpCsBF2zdrSf9BM9VvJta2RrtiwxS5cxeGaLixNuB6RBoCjlQCOQdnDu3GQGGj-qNoQ260h02R3EIwPo77WndDOKZ7XxVlA/s640/1shield.png" width="640" /></a></div><br />The player shield mechanic behaves much like UFPS's player weapons, in fact I duplicated the vp_FPWeaponHandler script, renamed it FPShieldHandler, and commented out a few lines pertaining to weapon behavior.<br /><br />This allowed me to render a player shield (Bronze Shield from the Skeleton pack plus hand model from UFPS) alongside player weapons. &nbsp;However I couldn't get states to behave as they do for weapons, and without the editor script the inspector panel for my shield wasn't so user friendly. <br /><br />After a few days attempting to replicate the rest of the UFPS weapon system I decided to stick to what I know and manipulate the shield behavior with uScript. &nbsp;The resulting ShieldHelper graph is certainly my largest yet, it makes the shield respond to the UFPS player states run, crouch, and dead (as well as walking). &nbsp;It also checks for a "Block" button input and raises the shield (unless stamina is depleted) as seen in the above screenshot.<br /><br />The actual damage blocking mechanic is similar to my AI armor mechanic in that it puts an invisible collider in front of the shield which stops incoming damage. &nbsp;Although for some reason the UFPS player controller didn't like having a collider so close to it, and caused the player to move erratically, even when I made the shield collider ignore collisions with the player. &nbsp;So instead of using a damage handler like AI armor, I made the shield collider a trigger which solved the issue with the player controller, and I added on-trigger logic to it that cancels AI damage, applies recoil to the shield, and subtracts from the stamina value.Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-86884788336013535992013-12-27T09:51:00.000-05:002014-01-03T10:01:07.321-05:00Project Draconyx: Player Combat part 1, Stamina and PlanningYet again this step will be divided into smaller segments. &nbsp;Player combat will be composed of 4 segments:<br /><br /><ul><li><b>Stamina </b>(see below)</li><li><b>Blocking and Shields&nbsp;<a href="http://aaagd.blogspot.com/2013/12/project-draconyx-player-combat-part-1.html">done</a></b></li><li><b>Ranged Attacks <a href="http://aaagd.blogspot.com/2013/12/project-draconyx-player-combat-part-3.html">done</a></b></li><li><b>Melee Attacks <a href="http://aaagd.blogspot.com/2014/01/project-draconyx-player-combat-part-4.html">done</a></b></li></ul><div>Player stamina was much simpler than I expected (if you read this earlier it was at the bottom of the list), entirely due to <a href="http://visionpunk.vanillaforums.com/discussion/175/stamina-bar/p1" target="_blank">b1nary</a> from the VisionPunk forums for providing a stamina script. &nbsp;Attaching the script to the Player worked like a charm, and it only took a matter of minutes to incorporate the stamina system into my shield script.</div><ul></ul>Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-76937374469654392282013-12-20T09:25:00.000-05:002013-12-20T09:25:32.438-05:00Project Draconyx: AI part 4, Stats and SpawningFor the AI stats we'll need to reference some things that don't exist yet, so the first thing I did was make an empty game object named PlayerStats and attached it to the DataManager. &nbsp;I made a script with a few exposed variables, Player Level, DifficultyModifier, and DamageModifier, in the player stats segment we'll add more logic to this, but right now it works for testing.<br /><br />Damage was easy, I added a few nodes to the previous AIDamage graphs so that we set a BaseDamage value, which gets multiplied with the DamageModifier to output true damage.<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPyJIQryFjiEB153ktQ4CqZNrAsXJPsJKo0xCxIR9Wo5W7XtQufKNRgjSnzlIlZtbcZ54EAGc3nXrDkSeOJ0JZyheWZNuCv-mnBSmuJm3uVIpsgfMXC_fPaxxWG8w6O_fP0LvaeDAhAyI/s1600/1damagelevel.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="257" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgPyJIQryFjiEB153ktQ4CqZNrAsXJPsJKo0xCxIR9Wo5W7XtQufKNRgjSnzlIlZtbcZ54EAGc3nXrDkSeOJ0JZyheWZNuCv-mnBSmuJm3uVIpsgfMXC_fPaxxWG8w6O_fP0LvaeDAhAyI/s400/1damagelevel.png" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Determining AI level is a little more complicated, but it's still a simple process. &nbsp;We have 3 AI levels, easy, medium, and hard, when an encounter is spawned it will check the PlayerLevel and DifficultyModifier to determine which level AI to use.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSDhm4XA-M0QtcGbIatIPNHxitS2cpuXeefaQsVFBgBEIGPaN4xqD_NyRwdme-7KG8_fGxp_SPzjul3gqiSNwzzz0k3EpglgHpwxEcs8jJWx7XOEJyLhylmQEGAye0AT_0RAcI-n6OMYE/s1600/2encounterlevel.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="305" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiSDhm4XA-M0QtcGbIatIPNHxitS2cpuXeefaQsVFBgBEIGPaN4xqD_NyRwdme-7KG8_fGxp_SPzjul3gqiSNwzzz0k3EpglgHpwxEcs8jJWx7XOEJyLhylmQEGAye0AT_0RAcI-n6OMYE/s400/2encounterlevel.png" width="400" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Obviously higher level AI will have better stats, but we can also use this with the&nbsp;<a href="http://aaagd.blogspot.com/2013/12/project-draconyx-level-mock-ups.html">Randomizer</a> script to influence armor chance, on the shield for example we could use RandomDelete, the Easy Skeleton could have a 90% chance to delete the shield, Medium would have 50%, and Hard 10%.</div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMa3W5ndJInHwVDNa0FeyGNlQXOt-DLs6T6RqAaPT85rmC90_qgNLAsuph8wj0l8nKl5eSqCpUTPJHc5BdqvVkgez0Vc48i4HLt50IQJ9IEFzCae5Vks-BMStiP8abBLCE_h-1YU_FDPM/s1600/3encounterlogic.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="171" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjMa3W5ndJInHwVDNa0FeyGNlQXOt-DLs6T6RqAaPT85rmC90_qgNLAsuph8wj0l8nKl5eSqCpUTPJHc5BdqvVkgez0Vc48i4HLt50IQJ9IEFzCae5Vks-BMStiP8abBLCE_h-1YU_FDPM/s640/3encounterlogic.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div>&nbsp; With this system a level 1 player on normal difficulty (modifier value of 1) would have a 0% chance of encountering a Hard Skeleton, a 1.5% chance of Medium, and 98.5% of Easy. &nbsp;At level 5 the Hard chance would be at 5% and Medium at 7.5% (Easy is always the remainder of the other two, well technically it's more complicated, but I'm not a mathematician), and by level 25 the Hard chance would be 25% and medium 37.5%. <br /><br />I'm not yet sure how high Player Level will go, or how fast leveling will be, so I might have to modify the logic later on.Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-5561959004224333572013-12-19T08:41:00.000-05:002013-12-19T08:41:37.474-05:00Project Draconyx: AI part 3, Armor and ShieldsThe armor system I have devised is quite simple, like melee attacks it will mount a collider to a point in the AI's hierarchy. &nbsp;We'll use the DamageHandler script from UFPS on the armor collider, this will allow the armor to absorb some damage before hurting the AI.<br /><br />I've created a layer named Armor and set it to ignore collisions with the AI layer, then I made an AIArmor graph in uScript.<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGS2RWzi17N_kCIzsITjsTIVSR2roNHOkU1roB-1aN1JDjdD0oAlyXomLuqZ79Vm4ZwaN_u6IhEVx02UgoJSsNC0xGqkPN7hc_tYoUc2lLfc3KlZA7WLGsttvMAY0kdj0nY2960D9A_nU/s1600/1armor.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="356" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjGS2RWzi17N_kCIzsITjsTIVSR2roNHOkU1roB-1aN1JDjdD0oAlyXomLuqZ79Vm4ZwaN_u6IhEVx02UgoJSsNC0xGqkPN7hc_tYoUc2lLfc3KlZA7WLGsttvMAY0kdj0nY2960D9A_nU/s640/1armor.png" width="640" /></a></div>The logic moves the armor to the MountPoint every frame until the AI is disabled or destroyed, then it checks whether to destroy the armor, or enable gravity. &nbsp;I made this check because when I get into player combat and inventory I would like the option to pickup the enemy's shield, but dropping other non-equipable armor items would just clutter up the scene.<br /><br />With the graph made we just need to add it to a collider in the armor layer and specify the MountPoint, and also add a DamageHandler script to the collider.<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisH5Z3_dvxKMyvyhuAcx8yojVSfbAhMlaXX43469o9oL86fVPLh9Fen8bGwbg7aMVb8bOi1AWQeNBvO7NB_rDUiKWfVk5VCm3M20uAPHP6pHF0aD0NIgC3ZMrlGmLnhVRtpqopcPI8bcM/s1600/2armor.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="224" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEisH5Z3_dvxKMyvyhuAcx8yojVSfbAhMlaXX43469o9oL86fVPLh9Fen8bGwbg7aMVb8bOi1AWQeNBvO7NB_rDUiKWfVk5VCm3M20uAPHP6pHF0aD0NIgC3ZMrlGmLnhVRtpqopcPI8bcM/s640/2armor.png" width="640" /></a></div>Here the shield with it's collider is a seperate armor object, this way if the player breaks the shield it will actually destroy it. &nbsp;The helmet however, is directly attached to the Skeleton, a separate sphere collider is used for the head armor. &nbsp;I also enabled respawn with a delay of 3-5 seconds on the HelmetCollider's DamageHandler, this way the player can never actually destroy the helmet but if you strike fast enough you can still kill the Skeleton with headshots (not that headshots do additional damage with this AI package, but it's an example of the armor system, the same logic could be applied to any piece of armor).Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-86734950863969825542013-12-18T11:28:00.000-05:002013-12-18T11:28:15.064-05:00Project Draconyx: AI part 2, Melee AttacksThe melee attack provided with UFPS AI uses the same hitscan script as the projectiles, this makes melee damage a single frame event rather than persisting for the duration of the attack animation, which means it's possible to dodge the damage while still getting hit by the animation. &nbsp;In order to correct this we first need a model with a melee animation, I will use the free&nbsp;<a href="http://u3d.as/content/bisaniyehocam/skeletons-pack/2fA">Skeletons Pack</a>&nbsp;by bisaniyehocam, which also includes shields that we can use in the next step.<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgPKUSbsfY75ee3UrRC2J9Jk7C0RXlrDi5Kcj2aBUwTAX0YE7MjUsiEIosgChbet7Nm2RYy-4BHrsrW_XL2HNIuQFGh7LRqo8O6ysmJo9pGSBzakbFzjV3debJLp5fX25Ajd-6r8uqcFo/s1600/1meleecollider.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="348" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjgPKUSbsfY75ee3UrRC2J9Jk7C0RXlrDi5Kcj2aBUwTAX0YE7MjUsiEIosgChbet7Nm2RYy-4BHrsrW_XL2HNIuQFGh7LRqo8O6ysmJo9pGSBzakbFzjV3debJLp5fX25Ajd-6r8uqcFo/s640/1meleecollider.png" width="640" /></a></div><div style="text-align: center;">We will make this collider active during melee attacks.</div><div style="text-align: center;"><br /></div><div style="text-align: left;">To control AI melee attacks I made 2 new graphs, AIMeleeHandler and AIMeleeDamage. &nbsp;AIMeleeHandler enables the attack when the melee animation is played and disables it after a duration, it is also responsible for setting the position of the collider as well as making sure the collider doesn't hit the AI that is performing the attack. &nbsp;AIMeleeDamage damages the object when a collision occurs, it also contains a failsafe to disable the collider if the AI is killed/disabled/destroyed in mid-attack.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwH3V6fl9nBTtsJwawXl9UCIU-5UKEKPud-zW9RsLEwbK4Wp-hiYLE4BLj7Q3uv0WZsDMjUmxyQ-LQhHd62GPHe52T6kup4LVumYEp55ieF84L2YPYTH_-n7DMvazScUg32u3_ljAJkOQ/s1600/2meleehandler.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="516" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwH3V6fl9nBTtsJwawXl9UCIU-5UKEKPud-zW9RsLEwbK4Wp-hiYLE4BLj7Q3uv0WZsDMjUmxyQ-LQhHd62GPHe52T6kup4LVumYEp55ieF84L2YPYTH_-n7DMvazScUg32u3_ljAJkOQ/s640/2meleehandler.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNmWbFjHQbtkQq9hsTtHjEE1VLNvzGRb49s-pj8goOnhkXi-j0SYfP1TDJzQezd5tox8l-21zLcN8y74CB_sL3X12Bxh3sZDnlt-QKIvF0mEVfh2UZ-F3wYrNCCTXjx6UEzUoi6dOuN_U/s1600/3meleedamage.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="542" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjNmWbFjHQbtkQq9hsTtHjEE1VLNvzGRb49s-pj8goOnhkXi-j0SYfP1TDJzQezd5tox8l-21zLcN8y74CB_sL3X12Bxh3sZDnlt-QKIvF0mEVfh2UZ-F3wYrNCCTXjx6UEzUoi6dOuN_U/s640/3meleedamage.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">Now we add AIMeleeDamage to a collider object, add AIMeleeHandler to the point in the Skeleton hierarchy where melee damage will occur, and fill in the values in the inspector.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjQjIkfnrWBtuSfhdPuohsJPcEmv5e7TRLgJMwHfsaHwO_wC41mi0b2OAhHH_WZn7Nw7NzJZwL9QxhHXloJvM-ptm-mFlUENY5BndpkRpRjXvXW_mkcrJiywdGK7r0LocKc8hOT0eaDIw/s1600/4meleehandler.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="378" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjjQjIkfnrWBtuSfhdPuohsJPcEmv5e7TRLgJMwHfsaHwO_wC41mi0b2OAhHH_WZn7Nw7NzJZwL9QxhHXloJvM-ptm-mFlUENY5BndpkRpRjXvXW_mkcrJiywdGK7r0LocKc8hOT0eaDIw/s640/4meleehandler.png" width="640" /></a></div><div style="text-align: left;"><br /></div><div style="text-align: left;">Now the Skeleton's dagger can deal damage whenever the attack animation plays. &nbsp;By using animations to activate the collider we could make multiple melee points for different animations while maintaining proper local damage. &nbsp;A punch applies damage from the fist, a kick from the foot, bite from the mouth, etc.</div>Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-1907892338820019532013-12-17T10:45:00.000-05:002013-12-17T10:59:51.046-05:00Project Draconyx: AI part 1, Projectile AttacksUFPS AI uses a hitscan projectile by default, this won't do as most of the projectiles in Draconyx are likely to be non-instant attacks like arrows or spells. &nbsp;I have made a projectile graph to handle this, it can be used with rigidbodies that are or are not affected by gravity.<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhy8zO9ZFY2e31IN1mI21VdPb5TdaoyiakQD4jtbrV1WkL5KiyGlk1qVjkjth9sPwJnlgEY_BDUgO4wsTC3aQzuC0zz6qRJKK7u5fo8usyDf4K1wIc8PuxT7G2etFd3EImwpcXzQvDgnUY/s1600/1projectilegraph.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhy8zO9ZFY2e31IN1mI21VdPb5TdaoyiakQD4jtbrV1WkL5KiyGlk1qVjkjth9sPwJnlgEY_BDUgO4wsTC3aQzuC0zz6qRJKK7u5fo8usyDf4K1wIc8PuxT7G2etFd3EImwpcXzQvDgnUY/s640/1projectilegraph.png" width="545" /></a></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7p_xGR4SARaSKHO0sD4Nj-2f2mc91F6S-rPzx-lykUMoNxjasCfehptgVN99FuCSFjooW5dzen-bt85furePysXNDX3Ry3EThuU-iPFBmcFEm5R3m564eNd2ZYzPyhgQpZmtT-iP5KJk/s1600/1projectilegraph2.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="540" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj7p_xGR4SARaSKHO0sD4Nj-2f2mc91F6S-rPzx-lykUMoNxjasCfehptgVN99FuCSFjooW5dzen-bt85furePysXNDX3Ry3EThuU-iPFBmcFEm5R3m564eNd2ZYzPyhgQpZmtT-iP5KJk/s640/1projectilegraph2.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;">After much experimentation I have concluded that using rigidbody force/velocity to propel an object produces inconsistent trajectories, so I used Control GameObject (move) to handle forward propulsion. &nbsp;The rigidbody only handles gravity and only in the Y axis.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPZ-LUFOBQdspzphpxr_Wh4gD-fP-331bpBqsAqd-bEWdN1_iBQ3ekhQ3MJJ9rJk-l96pKK_0ojl7hY4O6cG-j97WpZ1pcHRoMt0UDcsAhG1tObQWyuR96ODNhlbvF-QH7-dgR5ePdo1o/s1600/2projectile.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="408" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjPZ-LUFOBQdspzphpxr_Wh4gD-fP-331bpBqsAqd-bEWdN1_iBQ3ekhQ3MJJ9rJk-l96pKK_0ojl7hY4O6cG-j97WpZ1pcHRoMt0UDcsAhG1tObQWyuR96ODNhlbvF-QH7-dgR5ePdo1o/s640/2projectile.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;">Here the same script is used to create 2 different example projectiles. &nbsp;The MagicBall on the left does not use gravity (I forgot to freeze the Y axis, but it will still travel in a straight line), instead of dealing direct damage on collision, it spawns the CubeExplosion prefab from UFPS, which creates an AoE blast (which is absolutely ludicrous when added to the UFPS soldier's full-auto M4). &nbsp;The PhysicsBall on the right travels in an arc, and directly deals damage on impact.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">That wraps up projectiles, they work fine with UFPS's provided soldier character, but we'll be importing a new model with better melee animations for the next step.</div>Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-12199952353156924622013-12-17T09:32:00.000-05:002013-12-20T09:27:14.838-05:00Project Draconyx: AI planningAgain I will be breaking up the AI step into smaller installments. &nbsp;For this project I will be using the&nbsp;<a href="http://u3d.as/content/vision-punk/ai-add-on-for-ufps/5rL">AI Add-on for UFPS</a>, this may not be the most full featured AI asset available (it is in beta so I will try to hold my criticisms) but it has several advantages. &nbsp;It's cheap and is compatible with UFPS and A* Pathfinding Project right out of the box. &nbsp;It even has built in wander behavior, which is a huge plus for procedural levels and a feature lacking on many other AI solutions.<br /><br /><ul><li>Projectile attacks&nbsp;<a href="http://aaagd.blogspot.com/2013/12/project-draconyx-ai-part-1-projectile.html">done</a></li><li>Melee attacks&nbsp;<a href="http://aaagd.blogspot.com/2013/12/project-draconyx-ai-part-2-melee-attacks.html">done</a></li><li>Armor and Shields&nbsp;<a href="http://aaagd.blogspot.com/2013/12/project-draconyx-ai-part-3-armor-and.html">done</a></li><li>Stats and Spawning&nbsp;<a href="http://aaagd.blogspot.com/2013/12/project-draconyx-ai-part-4-stats-and.html">done</a></li></ul>Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-82492523631305162512013-12-14T11:05:00.000-05:002013-12-14T12:06:32.382-05:00Project Draconyx: Level Mock-ups ConclusionI have decided to postpone inclusion of a maze level generator for the time being. &nbsp;The logic would take me a long time to develop on my own, and while there are dungeon/maze generator assets available for Unity, I believe that it is best to move forward with the other areas of production before investing more time into level generation. &nbsp;In the meantime, I can achieve maze-like levels with my branch level builder:<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeefeGB3wCYbFE2-ZKLJ9GcFKWZPXkGqafgkzOqcYlnyaf05TKW92WmHybiOQzXGV4V6rexAgGq3CH2al2F2VwKpyHcVWhALmVOAsutJuPFLpqwEKPZd3RxZU1e-qCA0tKHWlX8VKL-nQ/s1600/1branchmaze.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="293" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgeefeGB3wCYbFE2-ZKLJ9GcFKWZPXkGqafgkzOqcYlnyaf05TKW92WmHybiOQzXGV4V6rexAgGq3CH2al2F2VwKpyHcVWhALmVOAsutJuPFLpqwEKPZd3RxZU1e-qCA0tKHWlX8VKL-nQ/s400/1branchmaze.png" width="400" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">I have also created a generic Randomizer graph, which can be used to add additional variation to any level. &nbsp;This uses methods similar to what I used in many of the branch and open level graphs.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIcLaDZxwxV3cNy_RjvGeaDGxZolcFZifGvDppZuXSMIoMDnWLGCuYSCGb7dMcH-QmGdMRkoK0k4_K9LChcUyuE8SBYGSjk1tA_9bgFtfAruVZUQEHXhXT20RHSHVnY2tS52_8rodQxgY/s1600/2randomizer.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="398" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgIcLaDZxwxV3cNy_RjvGeaDGxZolcFZifGvDppZuXSMIoMDnWLGCuYSCGb7dMcH-QmGdMRkoK0k4_K9LChcUyuE8SBYGSjk1tA_9bgFtfAruVZUQEHXhXT20RHSHVnY2tS52_8rodQxgY/s640/2randomizer.png" width="640" /></a></div>The Randomizer has 3 functions, Delete, Rotate, and Spawn, which can all work together or independently. &nbsp;An example of this would be furniture, we could add a table to one of the room prefabs in the above scene, but then every instance of that room would have the exact same table in the exact same position. &nbsp;If we add the Randomizer script to the table and enable RandomDelete, then some of the rooms would have a table and some wouldn't, a higher DeleteChance (0-100) would make less tables. &nbsp;Then we could enable RandomRotate, now every table that doesn't get deleted will be pointed in a random direction. &nbsp;Alternatively we could use a furniture-node (cube or any dummy object) with the Randomizer script, if we enable RandomSpawn and populate the SpawnObjects list with furniture then the room would contain something randomly picked from the list. &nbsp;With Delete, Rotate and Spawn all active the room could contain nothing, or an item from the list facing a random direction.Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-10277009435706708772013-12-13T14:26:00.001-05:002013-12-13T14:27:09.062-05:00Project Draconyx: Level Mock-ups part 3, Branch LevelsTo randomize branch levels I have constructed a node-based formula, where prefabs are spawned with nodes attached, and those nodes communicate with a builder component to determine which prefab to spawn next.<br /><br />Again, the uScript graphs are fairly large and lacking comments, so I will instead display screenshots depicting how the code works.<br /><br /><div class="separator" style="clear: both; text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1jlx0dwAjXVdCrEmicAS058GifEeE2QdsdF7_xbFAhxB8_NIUTZFcRV9EDDIjadN13hJmFDU75VKrws-gfCaf9dhHMWXte1MJViqeqcIuhx0pCjRDb6mn55LEUdqODRGTcn5gl_OblvE/s1600/1branchbuilder.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj1jlx0dwAjXVdCrEmicAS058GifEeE2QdsdF7_xbFAhxB8_NIUTZFcRV9EDDIjadN13hJmFDU75VKrws-gfCaf9dhHMWXte1MJViqeqcIuhx0pCjRDb6mn55LEUdqODRGTcn5gl_OblvE/s1600/1branchbuilder.png" height="640" width="337" /></a>&nbsp;The Builder is little more than a collection of variables, it holds the information that the nodes will use, and also starts construction by spawning a Start Prefab.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div style="text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_y6oNLk5UV236lgwQPTd6dT7oDaMCUAIQ5J0JKZZCJuGJmE-DErM4hTOoCK71HBAtDLQuuMT_sQ8-s6vCrWTI1HYBkBzDD6Jshyjm1D1DrIisw8zDILTAUGOB1lcMXC66skvAwkC9WE4/s1600/2branchnodes.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_y6oNLk5UV236lgwQPTd6dT7oDaMCUAIQ5J0JKZZCJuGJmE-DErM4hTOoCK71HBAtDLQuuMT_sQ8-s6vCrWTI1HYBkBzDD6Jshyjm1D1DrIisw8zDILTAUGOB1lcMXC66skvAwkC9WE4/s1600/2branchnodes.png" height="433" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;">The passageNode will check Level Size and Path Split Chance, then either spawn a Passage Prefab, Split Prefab, or End Prefab, depending on the conditions.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">When a Split Prefab is spawned the secondary passage has a branchNode, which will spawn Side Branches. &nbsp;At the end of these branches a portalNode will check the Multiple Exit variables to determine whether or not to spawn a second exit portal.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">The encounterNodes check Encounter Chance to determine whether or not to spawn an Encounter.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">To scan the pathfinder the branch level uses a similar method as the open level, when the End Prefab is spawned it gives the scan command, however the Builder has a fail-safe to scan after 3 seconds if the End Prefab hasn't been created yet. &nbsp;Because the size and shape of the level can vary it was difficult getting the grid graph's size large enough to encompass all of the possibilities without tanking performance, so instead I added the ProceduralGridMover script from A* to keep pathfinding centered on the player.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEHjAZ_cPpz3jWsVs0M3jPhHyVlaRaOzSj-YarXQ7zrpeff39QvXsVxQWpbTU9upYLBufSbODFlKtdVDOvLsQg65WbqT0ER-IVDD5VJr0ORK0RV54FlniubiL92uvKLBV6cusyBbl7wt8/s1600/3branchlevel.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjEHjAZ_cPpz3jWsVs0M3jPhHyVlaRaOzSj-YarXQ7zrpeff39QvXsVxQWpbTU9upYLBufSbODFlKtdVDOvLsQg65WbqT0ER-IVDD5VJr0ORK0RV54FlniubiL92uvKLBV6cusyBbl7wt8/s1600/3branchlevel.png" height="308" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;">A birds-eye view of the generated level.</div>Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-64957023525353095082013-12-11T16:40:00.000-05:002013-12-11T16:40:07.036-05:00Project Draconyx: Level Mock-ups part 2, Open LevelsFor the randomization in open levels I've decided to use a chain of deactivated objects, each object will execute it's code and then activate the next object. &nbsp;This process is designed to control spawn order (create high priority objects like portals first, then find room for low priority objects like trees), while providing a modular system (I could use a dozen spawners, or just a few). &nbsp;Theoretically I could have done the same thing with signals or custom events, but I figure using deactivated objects will help keep the message traffic down, subsequently lowering the chance of crossed signals.<br /><br />I won't be showing the uScript graphs this time, because most are quite long and scarcely commented. &nbsp;Instead I will be showing the inspector views, with example values used in the exposed variables.<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwTLnHlvHeMZiFkKuyjm9ZW2Lj8rdp7DsbTsHZ5B0-xG1EVWm3Nr8e9vsO_4kADlZK3S2SJbQdYEGV7ZX5BsVtIwFUPysx-Ndd_7LXswUs8Te-eLWsovMhI9851erstAMWuKxts30q_bs/s1600/1levelmngr.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="213" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhwTLnHlvHeMZiFkKuyjm9ZW2Lj8rdp7DsbTsHZ5B0-xG1EVWm3Nr8e9vsO_4kADlZK3S2SJbQdYEGV7ZX5BsVtIwFUPysx-Ndd_7LXswUs8Te-eLWsovMhI9851erstAMWuKxts30q_bs/s400/1levelmngr.png" width="400" /></a></div><br />The openLevelStart script simply activates the first link in the chain (1Portal Spawner) when the level is loaded. &nbsp;The AStar and Last To Activate variables are used in a fail-safe, if the last object in the chain isn't activated within 3 seconds it will send a "scan" command to the pathfinder so that the AI can begin to navigate the level even if it isn't finished loading (not a problem with the test level, but larger levels and more details could potentially bog things down).<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCBFUmKF2F7DqrSu7tnTN-8Z5tMo0dZxz2oqt_g1bOanC1ceGxKcBVSKgnI0CTmCnpi95uSpn4XM2t2qG87mK3U1FCCg7am8-sR8-xbJD6hjjD8NA5rEKXleGe6Q_TuZ5DNu3gsKROeNk/s1600/2portalspawn.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="342" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiCBFUmKF2F7DqrSu7tnTN-8Z5tMo0dZxz2oqt_g1bOanC1ceGxKcBVSKgnI0CTmCnpi95uSpn4XM2t2qG87mK3U1FCCg7am8-sR8-xbJD6hjjD8NA5rEKXleGe6Q_TuZ5DNu3gsKROeNk/s400/2portalspawn.png" width="400" /></a></div><br />The openLevelPortals script is responsible for placement of the portals, each portal will be randomly placed within separate zones so we can keep the start and end points in opposite parts of the map. &nbsp;Max Angle is part of a check to make sure the portals won't spawn in a location that the player can't access. &nbsp;When the portals are spawned it activates the next link in the chain (2Encounter Spawner).<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS1ZdPNU1fKYPzI8S_i1DJK3UYc6bJJkyy3fx0LHey2d8cEJhWwEqadGI5cGB_j30_a2PopIgCtZlrnb3ngkY-c1sJTQknZjnJ3RGeLaLjBMBq2kLz-mlZl8CbL2PNwd1HjU34Yfmpluw/s1600/3encounterspawn.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="400" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjS1ZdPNU1fKYPzI8S_i1DJK3UYc6bJJkyy3fx0LHey2d8cEJhWwEqadGI5cGB_j30_a2PopIgCtZlrnb3ngkY-c1sJTQknZjnJ3RGeLaLjBMBq2kLz-mlZl8CbL2PNwd1HjU34Yfmpluw/s400/3encounterspawn.png" width="395" /></a></div><br />The openLevelEncounter script acts much like the portal spawner above, except we are free to list as many Encounters and Encounter Zones as we wish. &nbsp;The Min/Max Encounter values determine how many total encounters will spawn in the level, and Encounter Zone R(adius) determines how far from a zone an encounter can be. &nbsp;This can be used to keep encounters away from the portals, so the player isn't mauled by mobs right away. &nbsp;Then it continues the chain by activating the next link.<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzQT3ZtF-ZhpkfOeChh-H-dC79PQ2MNeXk5KrWfG64q5W-BuQ0uE9Mp_xY2jUfYHQ1O4pYhTL60yEupu-8yW2ocpgG5uIgz3ek0B63cPK44JddVtYk052PfBtK4g6PP8Wz0EfixxR6kHo/s1600/4objectspawn.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="273" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhzQT3ZtF-ZhpkfOeChh-H-dC79PQ2MNeXk5KrWfG64q5W-BuQ0uE9Mp_xY2jUfYHQ1O4pYhTL60yEupu-8yW2ocpgG5uIgz3ek0B63cPK44JddVtYk052PfBtK4g6PP8Wz0EfixxR6kHo/s640/4objectspawn.png" width="640" /></a></div><br />The openLevelObjects scripts will spawn a random number of objects between Object Min and Max across the Map Size values. &nbsp;This is an example of the modular design, I could add a new inactive object "6Shrub Spawner" with another openLevelObject script, and by simply changing the Activate values it would still all link together<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitEQDZTRSq7jKYPkABwiB8-c8AyyVx7POIKkU0XCp6_dDIdLXbS11ab0rxMepUIuu-GpCC5aVkOZHNLnpJHVVzFoaF5vaTzW-Hp_HCOHKeQIbVCcFDOwdgcsvGwlbORkZZfMJ-w1S7H5c/s1600/5astarscan.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="185" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEitEQDZTRSq7jKYPkABwiB8-c8AyyVx7POIKkU0XCp6_dDIdLXbS11ab0rxMepUIuu-GpCC5aVkOZHNLnpJHVVzFoaF5vaTzW-Hp_HCOHKeQIbVCcFDOwdgcsvGwlbORkZZfMJ-w1S7H5c/s400/5astarscan.png" width="400" /></a></div><br />At the end of the chain the AStarScan object is activated, which tells the pathfinder to scan so that all of the dynamic objects can be added to the grid.<br /><br />The above examples create this scene, which is not a representation of the final style, but merely a test of the code.<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKyy1wPABOSti90SJS9I1Y1mSMWklDYXchxP6vQ9RxINv-csN0CkYhDt9pr1mEG66N0VYk-uz49ztmUU2M5Mj4CaMyd6t1hgB_I94MCY8N-7D0LAnv_yluWoDyyO23bq2WdpoDeic66QA/s1600/openlevel.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="306" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjKyy1wPABOSti90SJS9I1Y1mSMWklDYXchxP6vQ9RxINv-csN0CkYhDt9pr1mEG66N0VYk-uz49ztmUU2M5Mj4CaMyd6t1hgB_I94MCY8N-7D0LAnv_yluWoDyyO23bq2WdpoDeic66QA/s640/openlevel.png" width="640" /></a></div><br />Here we see a MineBot encounter, and in the background is the exit portal. &nbsp;The terrain and background are static, but the trees and grass, along with the encounter and portal, have all been randomly placed on the map.Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-57561769442287304742013-12-09T11:35:00.000-05:002013-12-14T11:19:23.705-05:00Project Draconyx: Level Mock-ups part 1, Planning and PathfindingI've decided to break up this step into smaller, more manageable segments. &nbsp;My plan is to use 3 level types, open, branch, and maze, each following segment will cover one level type.<br /><br /><div style="text-align: center;"><b>Open Levels&nbsp;<a href="http://aaagd.blogspot.com/2013/12/project-draconyx-level-mock-ups-part-2.html" target="_blank">done</a></b></div><div style="text-align: left;">Open levels are large, open areas where the player is free to wander and explore in any direction (within the level boundaries). &nbsp;The randomization in these levels will mainly be the encounters and object placement. &nbsp;An example would be a forest level, the trees would be randomly placed throughout the level and it could be home to a pack of wolves, an angry giant, a family of spiders, etc.</div><div style="text-align: left;"><br /></div><div style="text-align: center;"><b>Branch Levels&nbsp;<a href="http://aaagd.blogspot.com/2013/12/project-draconyx-level-mock-ups-part-3.html" target="_blank">done</a></b></div><div style="text-align: left;">Branch levels are more linear with diverging paths. &nbsp;The branches will be randomized, these paths may dead-end or lead to additional portals. &nbsp;An example would be a cave system, the player is presented with a fork-in-the-road, growling can be heard from one direction, perhaps the beast within is guarding a cache of treasure, or maybe there's nothing to be found but a fight. &nbsp;The other tunnel is better illuminated, and looks like it might lead to an exit, but maybe the light isn't an exit, it could be a bandit camp with a dozen cutthroats lying in wait.</div><div style="text-align: left;"><br /></div><div style="text-align: center;"><b>Maze Levels&nbsp;<a href="http://aaagd.blogspot.com/2013/12/project-draconyx-level-mock-ups.html" target="_blank">postponed</a></b></div><div style="text-align: left;">Certainly the most complex, maze levels are a web of interconnected rooms and corridors. &nbsp;An example would be a castle dungeon, the player must break free, but the dungeon is haunted by it's previous inhabitants. &nbsp;Each new room could be the way out, or it could be a tomb where reanimated skeletons await to exact vengeance on the living.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">One thing all these levels share is that they contain AI entities which will traverse them, in my experience when it comes to proceduraly generated scenes it's much easier to build around pathfinding rather than try to work it in later. &nbsp;Unity NavMesh won't do as it requires baking the mesh in-editor, crippling procedural level design, so before I get into level mock-ups we'll need to import a pathfinding asset.</div><div style="text-align: left;"><br /></div><div style="text-align: center;"><b>A* Pathfinding Project</b></div><div style="text-align: left;">I chose A* Pathfinding by Aron Granberg because it has enough flexibility and customization to fit nearly any project. &nbsp;I'll be using the&nbsp;<a href="http://www.arongranberg.com/astar/download" target="_blank">Free Version Beta</a>&nbsp;to keep the cost of this project down, which does introduce a few limitations, but I believe this is one of the best free assets available for unity.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">In preparation for the level mock-ups, I made an A* element using a grid graph and populated with values that should work for my project, then saved it as a prefab. &nbsp;Now I can drop the prefab into my scenes, and I only need to adjust the size of the graph to match the potential size of the level. &nbsp;I'll need to send a "scan" signal to A* after level generation, then I can use the provided MineBot prefab to test pathfinding.</div>Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-33065828826003240942013-12-07T14:43:00.000-05:002013-12-07T14:43:31.195-05:00Project Draconyx: Main Menu Level and Starting LevelThis step was a simple one, I imported one additional asset,&nbsp;<a href="http://u3d.as/content/tasharen-entertainment/ngui-next-gen-ui/2vh" target="_blank">Next-Gen UI</a>&nbsp;by Tasharen Entertainment, a vast improvement over the default GUI in both appearance and accessibility. &nbsp;I saved prefabs of the Player, Data Manager, Portal and Portal Exit objects from the previous step, then deleted the test scenes.<br /><br />First I created a new scene and added an NGUI component. &nbsp;For now we'll use the provided Fantasy Atlas and Font, I just made a simple button titled "New Game" and attached the LoadLevelOnClick script to it.<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtZksl506jE49wV5CQuNOaKRmBzZFd0WEiaZ4D6jbE_4zbQAw4mphAyONFnDpBRKk2AE2yHCTmPOpJscFbrfrFMJlGzKeN1UoE39aAgsJEd5MjXJQi8978l7tLYLyXJKE50-1Rbxi_9CE/s1600/1mainmenu.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="246" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhtZksl506jE49wV5CQuNOaKRmBzZFd0WEiaZ4D6jbE_4zbQAw4mphAyONFnDpBRKk2AE2yHCTmPOpJscFbrfrFMJlGzKeN1UoE39aAgsJEd5MjXJQi8978l7tLYLyXJKE50-1Rbxi_9CE/s640/1mainmenu.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">The starting level is nearly as basic as the main menu, it's just a room with the player and a portal. &nbsp;From here the portals will link to randomized levels, but the first level will always be this one so we can easily set the persistent data on start.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi98XOAo3mNECY4nRucJ6k72WklsaG3vod3O7ZuuNsuVibFDkrTlwIq9qEsj6bBUfFudzV8gnyuKPCZZI6GFYjYiX_lFt-uqfcGap5X5NFA5qwchR9Weez5NmPqyXfREp-xWntxWCgW9Z8/s1600/2newgame.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="290" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi98XOAo3mNECY4nRucJ6k72WklsaG3vod3O7ZuuNsuVibFDkrTlwIq9qEsj6bBUfFudzV8gnyuKPCZZI6GFYjYiX_lFt-uqfcGap5X5NFA5qwchR9Weez5NmPqyXfREp-xWntxWCgW9Z8/s640/2newgame.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">That's really all there is to it, but the next step on the road map is sure to balance out the simplicity of this one.</div>Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-44377068301935132092013-12-06T16:34:00.002-05:002013-12-06T16:34:33.231-05:00Project Draconyx: Interaction Manager RedesignEarlier today I noticed an issue with my <a href="http://aaagd.blogspot.com/2013/12/project-draconyx-persistent-data-and.html" target="_blank">InteractionManager and PortalTrigger</a> graphs, the faulty logic would cause multiple objects (portals) to respond to a single activation. &nbsp;So I went back into the graphs and redesigned some bits.<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQrs99EaT0vJZy2JDWiMkrKa1c7axwOJag65wMLHAPA7ql2ogWUfHy3qRuqEgbNj27ASK2xI1ROJgYk8Cd9nPdkxNMqvEmFElQK-oWsivXHEI6MK5VU1nZLkPJ5toU7rGfNzt07_RbPQY/s1600/InteractionManager_20131206_15524205.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgQrs99EaT0vJZy2JDWiMkrKa1c7axwOJag65wMLHAPA7ql2ogWUfHy3qRuqEgbNj27ASK2xI1ROJgYk8Cd9nPdkxNMqvEmFElQK-oWsivXHEI6MK5VU1nZLkPJ5toU7rGfNzt07_RbPQY/s640/InteractionManager_20131206_15524205.png" width="564" /></a></div><br />The Interaction Manager now sends an input signal only to a specific target (third graph down), the manager is now also responsible for setting the InteractionText value, as well as the new interactTarget.<br /><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4tL86Jb5E9aSEEg9fuV0T9n6em5ZFfnTTNPKxqpkOuBYksR6jlmLC6x58S6nTgZ1g3XopkP7njS0ft_xH4cLsD4w2__R4R4FwkoDpJyWw57N-YkBN92xhv3tphnj2DiXt4aeqvIiAyhc/s1600/PortalTrigger_20131206_15510184.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="426" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEg4tL86Jb5E9aSEEg9fuV0T9n6em5ZFfnTTNPKxqpkOuBYksR6jlmLC6x58S6nTgZ1g3XopkP7njS0ft_xH4cLsD4w2__R4R4FwkoDpJyWw57N-YkBN92xhv3tphnj2DiXt4aeqvIiAyhc/s640/PortalTrigger_20131206_15510184.png" width="640" /></a></div>The activated portal receives the input signal and loads a new level. &nbsp;The portal now broadcasts an event which contains the data that Interaction Manager needs for InteractionText and interactTarget, and can send a signal which tells the manager to clear both values.<br /><br />There are now checks in the trigger events, which will prevent overlapping interactables from crossing signals, the first object to trigger will be the active object.Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-51245649775586883482013-12-06T14:08:00.001-05:002013-12-06T16:38:43.012-05:00Project Draconyx: Persistent Data and Transitioning Between LevelsI've completed the first step of my road map and I'm here to document my progress. &nbsp;Obviously the first thing is to create a new Unity project, after that we need to import the assets needed for this step. &nbsp;For now I'm using 3 assets, VisionPunk's&nbsp;<a href="http://u3d.as/content/vision-punk/ufps-ultimate-fps/2Jc" target="_blank">Ultimate FPS</a>, Detox Studio's&nbsp;<a href="http://u3d.as/content/detox-studios-llc/u-script-visual-scripting-tool/2fc" target="_blank">uScript Visual Scripting Tool</a>, and the free uScript expansion:&nbsp;<a href="http://u3d.as/content/hyen-app-llc/necronodicon/3pc" target="_blank">Necronodicon</a>&nbsp;by hyenApp/Maus.<br /><br /><div style="text-align: center;"><b>Ultimate FPS</b></div><div style="text-align: left;">There's a lot to like in this package, I couldn't make an FPS controller as smooth and in-depth as this on my own so that alone will cut production time and improve quality. &nbsp;I want to avoid butchering asset source code whenever possible so I think I will run into issues later on with the privatized structure of the event handler, but in the end the pros outweigh the cons.</div><div style="text-align: left;"><br /></div><div style="text-align: center;"><b>uScript and Necronodicon</b></div><div style="text-align: left;">uScript is another time saver, I can make graphs faster than I can write code, and with fewer errors. &nbsp;I have gotten into the habit of importing Necronodicon into every uScript project, there's no reason not to, in this case more options is certainly better.</div><div style="text-align: left;"><br /></div><div style="text-align: center;"><b>Persistent Data</b></div><div style="text-align: left;">With the assets loaded we are ready to begin. &nbsp;The first issue to address is persistent data, we want information like player stats to persist between levels. &nbsp;First we'll drop a UFPS player prefab into the scene and rename it "Player". &nbsp;The simplest solution would be to write a&nbsp;<a href="http://docs.unity3d.com/Documentation/ScriptReference/Object.DontDestroyOnLoad.html" target="_blank">DontDestroyOnLoad</a>&nbsp;script and attach a copy to each persistent object, however I want to keep track of these objects so that they do not stack when, for example the user returns to the main menu and starts a new game.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">My solution was to make a graph named "DataManager" to handle persistent data.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2vqlxAUNsE7vdigLnEZZrDPzBT5-4yfwHuvQccn4HNRi92sYlIpcN-5J-z3jOVrVYAFfM90u_5Thf7BWj3EASiKUv6fEW8m7JMf3GUpQL46XgNIcZ9TbWPhdlr9H_UPRsSf_Uk8OWfu8/s1600/1datamanager.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="543" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh2vqlxAUNsE7vdigLnEZZrDPzBT5-4yfwHuvQccn4HNRi92sYlIpcN-5J-z3jOVrVYAFfM90u_5Thf7BWj3EASiKUv6fEW8m7JMf3GUpQL46XgNIcZ9TbWPhdlr9H_UPRsSf_Uk8OWfu8/s640/1datamanager.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;">The script is attached to an empty game object also named DataManager, all persistent objects are stored in the PersistentData list. &nbsp;Because the list is marked public any script can modify it and then signal the DataManager to update, this means we could add an NPC that follows the player between levels, and remove it from the list if it dies. &nbsp;This also makes it easy to destroy the whole list when the player returns to the main menu. &nbsp;For now though we'll just add the Player and DataManager to the list in the inspector.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"><b>Transitioning Between Levels</b></div><div class="separator" style="clear: both; text-align: left;">I have decided on using one-way portals to advance to the next scene. &nbsp;Because the player cannot go back through an exit-portal we don't need to save the state of previous scenes, which would have increased memory usage and complicated persistent data.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">The simplest way to activate a portal would be to use a trigger collider and write a script to load a new level on trigger, however I want the player to activate the portal with a key press to make level transitions a deliberate player-driven action rather than the result of getting too close to the trigger zone. &nbsp;For that I made a new graph "InteractionManager" attached to an empty game object of the same name, and nested under DataManager to make it persistent. &nbsp;I am using UFPS's Input Manager, in order to get uScript to reflect the nodes we need to add the vp_Input prefab into the scene.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3EZEzBs8VrdBO02m9DKJjvp34Sb_MuSLn_MDiuUMCbQ89oYFe19TqxvC8H3ziS3gYdXc9x4l7jQnz9VyPVlSVVN0Fj1sq_OaPSw6QAu60WFUk4ZaQlj36tlv2C3-8WW0b3gFuam9-Gco/s1600/2interactionmanager.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="398" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi3EZEzBs8VrdBO02m9DKJjvp34Sb_MuSLn_MDiuUMCbQ89oYFe19TqxvC8H3ziS3gYdXc9x4l7jQnz9VyPVlSVVN0Fj1sq_OaPSw6QAu60WFUk4ZaQlj36tlv2C3-8WW0b3gFuam9-Gco/s640/2interactionmanager.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">"InteractionText" is a public variable that other objects can modify, this will display a prompt (when I make a custom HUD) and also acts as a sort of switch. &nbsp;The upper graph will broadcast an interact event when the conditions are met. &nbsp;The lower graph is a safety, if the InteractionText isn't cleared within 10 seconds it will clear itself, interactions should clear it in their own code, this is just a precaution.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Now we need a portal, for now I am using a sphere named "Portal" with a trigger collider, I've made another graph "PortalTrigger" to attach to it.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDlcF__rcXKTdW0Uil0UyrekyexPxe65BTuVpR6lRAojoH0os3BZUhRM6TpK_Atu-P2eq7HjTQ9Xhnh37zPCg3mI17yewoTZmPZFrEeAIUp94jjCIUh8puizOK0rFgG9zN8rMAKHIYiRk/s1600/3portaltrigger.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="442" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiDlcF__rcXKTdW0Uil0UyrekyexPxe65BTuVpR6lRAojoH0os3BZUhRM6TpK_Atu-P2eq7HjTQ9Xhnh37zPCg3mI17yewoTZmPZFrEeAIUp94jjCIUh8puizOK0rFgG9zN8rMAKHIYiRk/s640/3portaltrigger.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">Looking at this now I am thinking of a potential issue, if a scene were to contain multiple portals they would all activate at the same time regardless of which one is the trigger. &nbsp;EDIT: View the redesign&nbsp;<a href="http://aaagd.blogspot.com/2013/12/project-draconyx-interaction-manager.html" target="_blank">here</a>.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">The upper graph modifies the InteractionText value so that InteractionManager can broadcast an event. &nbsp;The lower graph receives the event and loads a new level when the conditions are met. &nbsp;Once I've made some level mockups I plan to add logic to pick a level at random.</div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">The final issue is to move the player to the exit portal after a new level is loaded. &nbsp;My exit portal is another sphere and the script is a simple one, UFPS documentation recommends a specific method to force player position and direction, which I replicated in uScript.</div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBmutVHydoUJzaVRwPfEKqrKAYAf4ZPL02Y-YHHVfov3ldNsGauQBbMxKqYLmvKBniYsB74j5ks_uGaHKbQg1cnTnkBY0TLiruiZLlQJ4UR81-MFw9n9h1ZlihsWOWNb96Uex-2SRpn8g/s1600/4portalexit.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="244" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgBmutVHydoUJzaVRwPfEKqrKAYAf4ZPL02Y-YHHVfov3ldNsGauQBbMxKqYLmvKBniYsB74j5ks_uGaHKbQg1cnTnkBY0TLiruiZLlQJ4UR81-MFw9n9h1ZlihsWOWNb96Uex-2SRpn8g/s640/4portalexit.png" width="640" /></a></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: left;">This moves the player to the position of the exit portal, and rotates the camera to match the portal's angle on the Y axis. &nbsp;The portal's X rotation will likely be zero anyway, but I've chosen to set the camera's X axis to zero regardless of the portal's angle (X is up/down so 0 is straight ahead, Y is left/right, and Z is "tilt" which the camera doesn't use).</div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: left;"><br /></div><div class="separator" style="clear: both; text-align: center;"></div><div class="separator" style="clear: both; text-align: left;"><br /></div>Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-44415511865342665562013-12-04T08:55:00.000-05:002014-01-24T09:17:49.328-05:00Project Draconyx: Road Map<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFq7y2FTn_bVLwgAXMzHVJOsLaobzpKNCx5-htX_O9tXCZogodX7KRRVRgDjBU1w_9HArAhhmmAyvjAMcYOW9EGpI0MQ162GV25EcuOm06MIC42HA9cjW3J6f8g4PZ9hnr7PcsmbXii3g/s1600/Draconyx_roadmap.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhFq7y2FTn_bVLwgAXMzHVJOsLaobzpKNCx5-htX_O9tXCZogodX7KRRVRgDjBU1w_9HArAhhmmAyvjAMcYOW9EGpI0MQ162GV25EcuOm06MIC42HA9cjW3J6f8g4PZ9hnr7PcsmbXii3g/s640/Draconyx_roadmap.png" height="330" width="640" /></a></div><br /><div style="text-align: left;">The proceeding is a loose list of goals and the approximate order in which I will attempt to complete them. &nbsp;Modifications to this list will inevitably occur as the project moves forward, I will update this entry accordingly.</div><div style="text-align: left;"><br /></div><div style="text-align: left;"><b>Persistent data and transitioning between levels.&nbsp;<a href="http://aaagd.blogspot.com/2013/12/project-draconyx-persistent-data-and.html" target="_blank">done</a></b></div><div style="text-align: left;"><b><br /></b></div><div style="text-align: left;"><b>Main menu level, starting level.&nbsp;<a href="http://aaagd.blogspot.com/2013/12/project-draconyx-main-menu-level-and.html" target="_blank">done</a></b></div><div style="text-align: left;"><b><br /></b></div><div style="text-align: left;"><b>Level mock-ups, randomization, pathfinder/navigation data.&nbsp;<a href="http://aaagd.blogspot.com/2013/12/project-draconyx-level-mock-ups-part-1.html" target="_blank">done</a></b></div><div style="text-align: left;"><b><br /></b></div><div style="text-align: left;"><b>AI behavior, stats, and spawning.&nbsp;<a href="http://aaagd.blogspot.com/2013/12/project-draconyx-ai-planning.html">done</a></b></div><div style="text-align: left;"><b><br /></b><b>Player combat.&nbsp;<a href="http://aaagd.blogspot.com/2013/12/project-draconyx-player-combat-planning.html">done</a></b><br /><b><br /></b><b>Player HUD.&nbsp;<a href="http://aaagd.blogspot.com/2014/01/project-draconyx-player-hud-planning.html">done</a></b><br /><b><br /></b></div><div style="text-align: left;"><b>Player skills and inventory.&nbsp;<a href="http://aaagd.blogspot.com/2014/01/project-draconyx-player-skills-and.html">done</a></b></div><div style="text-align: left;"><b><br /></b></div><div style="text-align: left;"><b>Miscellaneous features. </b><a href="http://aaagd.blogspot.com/2014/01/project-draconyx-miscellaneous-features.html"><b>in progress</b></a></div><div style="text-align: left;"><b><br /></b><b>Beta&nbsp;preparation.</b><br /><b><br /></b><b>Beta level design.</b><br /><b><br /></b></div><div style="text-align: left;"><b>Finalization of art, models, and sound.</b></div><div style="text-align: left;"><br /></div><div style="text-align: left;">I do not have a fixed timeline for any of the steps listed as I cannot accurately estimate how long any of them may take, however I would like to complete this project within one year if possible. &nbsp;<b>EDIT:</b> shortly after writing this I stumbled across <a href="http://forum.unity3d.com/threads/184797-The-answer-to-every-Can-it-be-done-and-I-ve-lost-my-way-post" target="_blank">this thread</a>&nbsp;on the Unity forums, and I resolved to release a functioning beta build within 12 weeks. &nbsp;After that I will decide how long to spend polishing it before moving on.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">Again I would like to mention that the image displayed is not from the development process of Draconyx, it is meant as a representation of the game's theme, made from a few Unity Assets in my collection. &nbsp;The next blog post will cover the first step on my road map, and will contain images from the process.</div><div style="text-align: left;"><br /></div>Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-10218180284653070812013-12-02T09:51:00.000-05:002013-12-02T11:40:25.306-05:00Project Draconyx: Concept and Challenges<div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhw8C6vsEjxUGDL0MTSGGsqP0D0Lh_ui-wrLq1QlLTGHAlQ5oRUvzEambJ_jzi4pn5BRPqPFhPWon66SUizRQ08B2D2BeXI4UuumSX3l8o9tRqN7lY60n8hWjg-wZFR2KTw0vqJuBPhf0Q/s1600/Draconyx_title1.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="299" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhw8C6vsEjxUGDL0MTSGGsqP0D0Lh_ui-wrLq1QlLTGHAlQ5oRUvzEambJ_jzi4pn5BRPqPFhPWon66SUizRQ08B2D2BeXI4UuumSX3l8o9tRqN7lY60n8hWjg-wZFR2KTw0vqJuBPhf0Q/s640/Draconyx_title1.png" width="640" /></a></div><br /><div style="text-align: center;"><b>Project Draconyx</b></div><div style="text-align: left;">Firstly excuse the simple image, it is not concept art or a screenshot from the game, just a crude experiment with particle effects in Unity, chosen to represent the atmosphere I am aiming for. &nbsp;I would like to tell you that the name Draconyx has some symbolic meaning, but in truth I picked it from a list of interesting dinosaur names. &nbsp;This is not a dinosaur game, I use said list specifically for naming projects, naming things has never been my strong suit. &nbsp;Draconyx is currently a place holder, the name on release may change.</div><br /><div style="text-align: center;"><b>Concept</b></div>I envision this project as an&nbsp;endless first person dungeon crawler in a dark fantasy setting. &nbsp;The player's objective will be to push ever onward, navigating procedurally generated labyrinthian levels while battling hordes of bloodthirsty enemies. &nbsp;The main focus is combat, however the game will also depend on exploration, looting and leveling. &nbsp;Project Draconyx draws inspiration from several games and genres, I will leave the labels and comparisons to others.<br /><br /><div style="text-align: left;">The initial target platform will be PC. &nbsp;Mac and Linux ports are likely, console ports are a possibility but I have not looked into that yet and probably wont until I am satisfied with the PC version. &nbsp;Mobile ports would be my last consideration, as that generally means sacrificing detail in favor of performance, and re-working the control scheme for touch.</div><br /><div style="text-align: center;"><b>Challenges</b></div><div style="text-align: left;">The greatest challenge will be keeping my ambitions in check, this is a one-man project from an amateur developer, and balancing that realization with my desire to create the most epic experience possible will be key to completing this project. &nbsp;Because the game will be endless and the levels randomized, quests, puzzles and encounters face the danger of quickly becoming repetitive, I will have to ensure that such features are minimized and/or implemented as dynamically as the levels themselves.</div><div style="text-align: left;"><br /></div><div style="text-align: left;">Next on the agenda is the road map.</div><div style="text-align: left;"><br /></div>Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.post-92137403359561966022013-11-30T11:00:00.000-05:002014-01-07T06:36:03.826-05:00ForewordI should kick this thing off with a disclaimer, the words "amateur" and "aspiring" are not enough to describe how little I know about game development. &nbsp;In truth I'm just a gamer who wants to try his hand at making games. &nbsp;If you came here hoping to learn some new tricks, I'm sorry to disappoint you but such revelations will likely be few and far between.<br /><br />Which brings up the issue, why start a blog if I don't have advice or guidance to offer? &nbsp;The simple answer is to keep myself focused and organized. &nbsp;This isn't my first attempt at making videogames, many previous attempts have fizzled out when I found myself with too many isolated game components in various states of completion, and no map to connect them. &nbsp;This time I have resolved to document every step of the process and set goals to keep me on track.<br /><br />To start my goals, the next post will layout a concept for my current project, followed by the development road map. &nbsp;I will be using Unity free for this project, as well as both free and purchased Unity Assets,&nbsp;<a href="http://unity3d.com/" rel="nofollow" target="_blank">unity3d.com</a>.<br /><br />I suppose that wraps up this little introduction, in closing insert something clever here.Mateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com0tag:blogger.com,1999:blog-3223852017784708240.page-16073335194598621012015-01-24T20:21:00.001-05:002015-01-24T20:21:37.717-05:00yesMateodonhttp://www.blogger.com/profile/14242693143634557911noreply@blogger.com