J.J.Cat's Blog
2024-01-08T14:18:43+00:00
http://www.jjcat.com
J.J. Cat
JJCat@Outlook.com
DOTween Sequence 使用图解
2014-11-26T00:00:00+00:00
http://www.jjcat.com/2014/11/26/DOTween_Sequence_使用图解
<p>最近在使用<a href="http://dotween.demigiant.com/documentation.php">DOTween</a>制作一些动画过渡的内容,发现非常好用,使用Sequence类可以方便的组织Tweens来制作复杂的过渡动画。Sequence的几个函数文档说明都比较简单,我列出每个函数调用后的Sequence变化以方便查阅。</p>
<p>下图表示调用函数前的Sequence。</p>
<p><img src="http://www.jjcat.me/image/2014/11/dotween1.png" alt="image" /></p>
<hr />
<p>###Append(Tween tween)
Adds the given tween to the end of the Sequence.</p>
<p>在Sequence的最后添加一个tween。</p>
<p><img src="http://www.jjcat.me/image/2014/11/dotween2.png" alt="image" /></p>
<hr />
<p>###AppendCallback(TweenCallback callback)
Adds the given callback to the end of the Sequence.</p>
<p>在Sequence的最后添加一个回调函数。</p>
<p><img src="http://www.jjcat.me/image/2014/11/dotween3.png" alt="image" /></p>
<hr />
<p>###AppendInterval(float interval)
Adds the given interval to the end of the Sequence.</p>
<p>在Sequence的最后添加一段时间间隔。</p>
<p><img src="http://www.jjcat.me/image/2014/11/dotween4.png" alt="image" /></p>
<hr />
<p>###Insert(float atPosition, Tween tween)
Inserts the given tween at the given time position, thus allowing you to overlap tweens instead than just placing them one after each other.</p>
<p>在给定的时间位置上放置一个tween,可以实现同时播放多个tween的效果,而不是一个接着一个播放。</p>
<p><img src="http://www.jjcat.me/image/2014/11/dotween5.png" alt="image" /></p>
<hr />
<p>###InsertCallback(float atPosition, TweenCallback callback)
Inserts the given callback at the given time position.</p>
<p>在给定的时间位置上放置一个回调函数。</p>
<p><img src="http://www.jjcat.me/image/2014/11/dotween6.png" alt="image" /></p>
<hr />
<p>###Join(Tween tween)
Inserts the given tween at the same time position of the last tween added to the Sequence.</p>
<p>在Sequence的最后一个tween的开始处放置一个tween。</p>
<p><img src="http://www.jjcat.me/image/2014/11/dotween7.png" alt="image" /></p>
<hr />
<p>###Prepend(Tween tween)
Adds the given tween to the beginning of the Sequence, pushing forward in time the rest of the contents</p>
<p>在Sequence开始处插入一个tween,原先的内容根据时间往后移。</p>
<p><img src="http://www.jjcat.me/image/2014/11/dotween8.png" alt="image" /></p>
<hr />
<p>###PrependCallback(TweenCallback callback)
Adds the given callback to the beginning of the Sequence.</p>
<p>在Sequence开始处插入一个回调函数。</p>
<p><img src="http://www.jjcat.me/image/2014/11/dotween9.png" alt="image" /></p>
<hr />
<p>###PrependInterval(float interval)
Adds the given interval to the beginning of the Sequence, pushing forward in time the rest of the contents.</p>
<p>在Sequence开始处插入一段时间间隔,原先的内容根据时间往后移。</p>
<p><img src="http://www.jjcat.me/image/2014/11/dotween10.png" alt="image" /></p>
Unity3d中制作Loading场景进度条所遇到的问题
2014-06-22T00:00:00+00:00
http://www.jjcat.com/2014/06/22/Unity3d中制作Loading场景进度条所遇到的问题
<h2 id="背景">背景</h2>
<p>通常游戏的主场景包含的资源较多,这会导致加载场景的时间较长。为了避免这个问题,可以首先加载Loading场景,然后再通过Loading场景来加载主场景。因为Loading场景包含的资源较少,所以加载速度快。在加载主场景的时候一般会在Loading界面中显示一个进度条来告知玩家当前加载的进度。在Unity中可以通过调用<code class="language-plaintext highlighter-rouge">Application.LoadLevelAsync</code>函数来异步加载游戏场景,通过查询<code class="language-plaintext highlighter-rouge">AsyncOperation.progress</code>的值来得到场景加载的进度。</p>
<h2 id="尝试遇到问题">尝试——遇到问题</h2>
<p>第一步当加载完Loading场景后,调用如下的<code class="language-plaintext highlighter-rouge">LoadGame</code>函数开始加载游戏场景,使用异步加载的方式加载场景1(Loading场景为0,主场景为1),通过Unity提供的Coroutine机制,我们可以方便的在每一帧结束后调用<code class="language-plaintext highlighter-rouge">SetLoadingPercentage</code>函数来更新界面中显示的进度条的数值。</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">public</span> <span class="k">void</span> <span class="nf">LoadGame</span><span class="p">()</span> <span class="p">{</span>
<span class="nf">StartCoroutine</span><span class="p">(</span><span class="nf">StartLoading_1</span><span class="p">(</span><span class="m">1</span><span class="p">));</span>
<span class="p">}</span>
<span class="k">private</span> <span class="n">IEnumerator</span> <span class="nf">StartLoading_1</span><span class="p">(</span><span class="kt">int</span> <span class="n">scene</span><span class="p">)</span> <span class="p">{</span>
<span class="n">AsyncOperation</span> <span class="n">op</span> <span class="p">=</span> <span class="n">Application</span><span class="p">.</span><span class="nf">LoadLevelAsync</span><span class="p">(</span><span class="n">scene</span><span class="p">);</span>
<span class="k">while</span><span class="p">(!</span><span class="n">op</span><span class="p">.</span><span class="n">isDone</span><span class="p">)</span> <span class="p">{</span>
<span class="nf">SetLoadingPercentage</span><span class="p">(</span><span class="n">op</span><span class="p">.</span><span class="n">progress</span> <span class="p">*</span> <span class="m">100</span><span class="p">);</span>
<span class="k">yield</span> <span class="k">return</span> <span class="k">new</span> <span class="nf">WaitForEndOfFrame</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>最后进度条的效果显示如下:</p>
<p><img src="http://i.imgur.com/kesstj7.gif" alt="" /></p>
<p>进度条并没有连续的显示加载的进度,而是停顿一下切换一个数字,再停顿一下切换一个数子,最后在没有显示100%就情况下就切换到主场景了。究其原因在于<code class="language-plaintext highlighter-rouge">Application.LoadLevelAsync</code>并不是真正的后台加载,它在每一帧加载一些游戏资源,并给出一个progress值,所以在加载的时候还是会造成游戏卡顿,<code class="language-plaintext highlighter-rouge">AsyncOperation.progress</code>的值也不够精确。当主场景加载完毕后Unity就自动切换场景,所以上述代码中的while循环体内的代码是不会被调用的,导致进度条不会显示100%。</p>
<h2 id="修补100完成">修补——100%完成</h2>
<p>为了让进度条能显示100%,取巧一点的办法是将<code class="language-plaintext highlighter-rouge">AsyncOperation.progress</code>的值乘上2,这样当加载到50%的时候界面上就显示100%了。缺点是当界面上显示100%的时候,用户还要等待一段时间才会进入游戏。其实Unity提供了手动切换场景的方法,把<code class="language-plaintext highlighter-rouge">AsyncOperation.allowSceneActivation</code>设为<code class="language-plaintext highlighter-rouge">false</code>就可以禁止Unity加载完毕后自动切换场景,修改后的<code class="language-plaintext highlighter-rouge">StartLoading_2</code>代码如下:</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="c1">// this function is not work</span>
<span class="k">private</span> <span class="n">IEnumerator</span> <span class="nf">StartLoading_2</span><span class="p">()</span> <span class="p">{</span>
<span class="n">AsyncOperation</span> <span class="n">op</span> <span class="p">=</span> <span class="n">Application</span><span class="p">.</span><span class="nf">LoadLevelAsync</span><span class="p">(</span><span class="m">1</span><span class="p">);</span>
<span class="n">op</span><span class="p">.</span><span class="n">allowSceneActivation</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>
<span class="k">while</span><span class="p">(!</span><span class="n">op</span><span class="p">.</span><span class="n">isDone</span><span class="p">)</span> <span class="p">{</span>
<span class="nf">SetLoadingPercentage</span><span class="p">(</span><span class="n">op</span><span class="p">.</span><span class="n">progress</span> <span class="p">*</span> <span class="m">100</span><span class="p">);</span>
<span class="k">yield</span> <span class="k">return</span> <span class="k">new</span> <span class="nf">WaitForEndOfFrame</span><span class="p">();</span>
<span class="p">}</span>
<span class="n">op</span><span class="p">.</span><span class="n">allowSceneActivation</span> <span class="p">=</span> <span class="k">true</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>
<p>我们首先将<code class="language-plaintext highlighter-rouge">AsyncOperation.allowSceneActivation</code>设为<code class="language-plaintext highlighter-rouge">false</code>,当加载完成后再设为<code class="language-plaintext highlighter-rouge">true</code>。代码看上去没有错,但是执行的结果是进度条最后会一直停留在90%上,场景不会切换。通过打印log发现<code class="language-plaintext highlighter-rouge">AsyncOperation.isDone</code>一直为<code class="language-plaintext highlighter-rouge">false</code>,<code class="language-plaintext highlighter-rouge">AsyncOperation.progress</code>的值增加到0.9后就保持不变了,也就是说场景永远不会被加载完毕。</p>
<p><img src="http://i.imgur.com/d44E3Yt.gif" alt="" /></p>
<p>在这个<a href="http://forum.unity3d.com/threads/using-allowsceneactivation.166106/#post-1146076">帖子</a>中找到了答案,原来把<code class="language-plaintext highlighter-rouge">allowSceneActivation</code>设置为<code class="language-plaintext highlighter-rouge">false</code>后,Unity就只会加载场景到90%,剩下的10%要等到<code class="language-plaintext highlighter-rouge">allowSceneActivation</code>设置为<code class="language-plaintext highlighter-rouge">true</code>后才加载,这不得不说是一个坑。所以代码改为如下。当<code class="language-plaintext highlighter-rouge">AsyncOperation.progress</code>到达0.9后,就直接把进度条的数值更新为100%,然后设置<code class="language-plaintext highlighter-rouge">AsyncOperation.allowSceneActivation</code>为<code class="language-plaintext highlighter-rouge">ture</code>,让Unity继续加载未完成的场景。</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">private</span> <span class="n">IEnumerator</span> <span class="nf">StartLoading_3</span><span class="p">()</span> <span class="p">{</span>
<span class="n">AsyncOperation</span> <span class="n">op</span> <span class="p">=</span> <span class="n">Application</span><span class="p">.</span><span class="nf">LoadLevelAsync</span><span class="p">(</span><span class="m">1</span><span class="p">);</span>
<span class="n">op</span><span class="p">.</span><span class="n">allowSceneActivation</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>
<span class="k">while</span><span class="p">(</span><span class="n">op</span><span class="p">.</span><span class="n">progress</span> <span class="p"><</span> <span class="m">0.9f</span><span class="p">)</span> <span class="p">{</span>
<span class="nf">SetLoadingPercentage</span><span class="p">(</span><span class="n">op</span><span class="p">.</span><span class="n">progress</span> <span class="p">*</span> <span class="m">100</span><span class="p">);</span>
<span class="k">yield</span> <span class="k">return</span> <span class="k">new</span> <span class="nf">WaitForEndOfFrame</span><span class="p">();</span>
<span class="p">}</span>
<span class="nf">SetLoadingPercentage</span><span class="p">(</span><span class="m">100</span><span class="p">);</span>
<span class="k">yield</span> <span class="k">return</span> <span class="k">new</span> <span class="nf">WaitForEndOfFrame</span><span class="p">();</span>
<span class="n">op</span><span class="p">.</span><span class="n">allowSceneActivation</span> <span class="p">=</span> <span class="k">true</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>
<p>最后的效果如下:</p>
<p><img src="http://i.imgur.com/BircPFa.gif" alt="" /></p>
<h2 id="打磨增加动画">打磨——增加动画</h2>
<p>上述的进度条虽然解决了100%显示的问题,但由于进度条的数值更新不是连续的,所以看上去不够自然和美观。为了看上去像是在连续加载,可以每一次更新进度条的时候插入过渡数值。这里我采用的策略是当获得<code class="language-plaintext highlighter-rouge">AsyncOperation.progress</code>的值后,不立即更新进度条的数值,而是每一帧在原有的数值上加1,这样就会产生数字不停滚动的动画效果了,迅雷中显示下载进度就用了这个方法。</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">private</span> <span class="n">IEnumerator</span> <span class="nf">StartLoading_4</span><span class="p">()</span> <span class="p">{</span>
<span class="kt">int</span> <span class="n">displayProgress</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
<span class="kt">int</span> <span class="n">toProgress</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
<span class="n">AsyncOperation</span> <span class="n">op</span> <span class="p">=</span> <span class="n">Application</span><span class="p">.</span><span class="nf">LoadLevelAsync</span><span class="p">(</span><span class="m">1</span><span class="p">);</span>
<span class="n">op</span><span class="p">.</span><span class="n">allowSceneActivation</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>
<span class="k">while</span><span class="p">(</span><span class="n">op</span><span class="p">.</span><span class="n">progress</span> <span class="p"><</span> <span class="m">0.9f</span><span class="p">)</span> <span class="p">{</span>
<span class="n">toProgress</span> <span class="p">=</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span><span class="n">op</span><span class="p">.</span><span class="n">progress</span> <span class="p">*</span> <span class="m">100</span><span class="p">;</span>
<span class="k">while</span><span class="p">(</span><span class="n">displayProgress</span> <span class="p"><</span> <span class="n">toProgress</span><span class="p">)</span> <span class="p">{</span>
<span class="p">++</span><span class="n">displayProgress</span><span class="p">;</span>
<span class="nf">SetLoadingPercentage</span><span class="p">(</span><span class="n">displayProgress</span><span class="p">);</span>
<span class="k">yield</span> <span class="k">return</span> <span class="k">new</span> <span class="nf">WaitForEndOfFrame</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="n">toProgress</span> <span class="p">=</span> <span class="m">100</span><span class="p">;</span>
<span class="k">while</span><span class="p">(</span><span class="n">displayProgress</span> <span class="p"><</span> <span class="n">toProgress</span><span class="p">){</span>
<span class="p">++</span><span class="n">displayProgress</span><span class="p">;</span>
<span class="nf">SetLoadingPercentage</span><span class="p">(</span><span class="n">displayProgress</span><span class="p">);</span>
<span class="k">yield</span> <span class="k">return</span> <span class="k">new</span> <span class="nf">WaitForEndOfFrame</span><span class="p">();</span>
<span class="p">}</span>
<span class="n">op</span><span class="p">.</span><span class="n">allowSceneActivation</span> <span class="p">=</span> <span class="k">true</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>
<p><code class="language-plaintext highlighter-rouge">displayProgress</code>用来记录要显示在进度条上的数值,最后进度条的动画如下:</p>
<p><img src="http://i.imgur.com/mAjrMuS.gif" alt="" /></p>
<p>对比第一种的进度条</p>
<p><img src="http://i.imgur.com/kesstj7.gif" alt="" /></p>
<h2 id="总结">总结</h2>
<p>如果在加载游戏主场景之前还需要解析数据表格,生成对象池,进行网络连接等操作,那么可以给这些操作赋予一个权值,利用这些权值就可以计算加载的进度了。如果你的场景加载速度非常快,那么可以使用一个假的进度条,让玩家看上几秒钟的loading动画,然后再加载场景。总之进度条虽然小,但要做好也是不容易的。</p>
<h3 id="参考">参考</h3>
<ol>
<li>阿高.<a href="http://blog.csdn.net/fg5823820/article/details/28913163">Unity 显示Loading(加载)进度 对于网上流行的方法进行更正</a></li>
<li>Unity3d官方论坛.<a href="http://forum.unity3d.com/threads/using-allowsceneactivation.166106/#post-1146076">using allowSceneActivation</a></li>
</ol>
使用NGUI 3.5.5创建高效的超大Scroll View
2014-04-05T00:00:00+00:00
http://www.jjcat.com/2014/04/05/使用NGUI_3.5.5创建高效的超大Scroll_View
<p>在使用NGUI的Scroll View的时候,碰到Scroll Item太多的话(超过100个),在滑动的过程中就会明显掉帧。究其原因就是NGUI每帧会对所有的Scroll Item进行更新,无论它是否显示。</p>
<p>NGUI 3.5.5加入了UIWrapContent,它会将不显示的Scroll Item设为disabled,这样就使得每帧更新的Scroll Item减少到当前显示的那几个,就再也不会出现掉帧的情况了。</p>
<p>不过UIWrapContent只能创建循环的Scroll View,不过只需要简单的几处修改,就能实现在普通的Scroll View上。</p>
<ol>
<li>
<p>复制一份<code class="language-plaintext highlighter-rouge">UIWrapContent.cs</code>,重命名为<code class="language-plaintext highlighter-rouge">UIBetterGrid.cs</code>,修改类名</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// line: 19 - 20, file: UIWrapContent.cs
[AddComponentMenu("NGUI/Interaction/Wrap Content")]
public class UIWrapContent : MonoBehaviour
</code></pre></div> </div>
<p>修改后的代码如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// line: 19 - 20, file: UIBetterGrid.cs
[AddComponentMenu("NGUI/Interaction/Better Grid")]
public class UIBetterGrid : MonoBehaviour
</code></pre></div> </div>
</li>
<li>
<p>修改初始化代码</p>
<div class="language-csharp highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// line: 52 - 54, file: UIWrapContent.cs</span>
<span class="n">mScroll</span><span class="p">.</span><span class="n">restrictWithinPanel</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>
<span class="k">if</span> <span class="p">(</span><span class="n">mScroll</span><span class="p">.</span><span class="n">dragEffect</span> <span class="p">==</span> <span class="n">UIScrollView</span><span class="p">.</span><span class="n">DragEffect</span><span class="p">.</span><span class="n">MomentumAndSpring</span><span class="p">)</span>
<span class="n">mScroll</span><span class="p">.</span><span class="n">dragEffect</span> <span class="p">=</span> <span class="n">UIScrollView</span><span class="p">.</span><span class="n">DragEffect</span><span class="p">.</span><span class="n">Momentum</span><span class="p">;</span>
</code></pre></div> </div>
<p>修改后的代码如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// line: 52 - 54, file: UIBetterGrid.cs
mScroll.restrictWithinPanel = true;
//if (mScroll.dragEffect == UIScrollView.DragEffect.MomentumAndSpring)
//mScroll.dragEffect = UIScrollView.DragEffect.Momentum;
</code></pre></div> </div>
</li>
<li>
<p>注释创建首尾循环的代码</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// line 159 - 170, file: UIBetterGrid.cs
//if (distance < -extents)
//{
// t.localPosition += new Vector3(extents * 2f, 0f, 0f);
// distance = t.localPosition.x - center.x;
// UpdateItem(t, i);
//}
//else if (distance > extents)
//{
// t.localPosition -= new Vector3(extents * 2f, 0f, 0f);
// distance = t.localPosition.x - center.x;
// UpdateItem(t, i);
//}
// line 190 - 201, file: UIBetterGrid.cs
//if (distance < -extents)
//{
// t.localPosition += new Vector3(0f, extents * 2f, 0f);
// distance = t.localPosition.y - center.y;
// UpdateItem(t, i);
//}
//else if (distance > extents)
//{
// t.localPosition -= new Vector3(0f, extents * 2f, 0f);
// distance = t.localPosition.y - center.y;
// UpdateItem(t, i);
//}
</code></pre></div> </div>
</li>
<li>
<p>修改<code class="language-plaintext highlighter-rouge">UIScrollView.cs</code></p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// line 173, file: UIWrapContent.cs
mBounds = NGUIMath.CalculateRelativeWidgetBounds(mTrans, mTrans);
</code></pre></div> </div>
<p>修改后的代码如下:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// line 173, file: UIBetterGrid.cs
mBounds = NGUIMath.CalculateRelativeWidgetBounds(mTrans, mTrans,true);
</code></pre></div> </div>
</li>
</ol>
<hr />
<p>最后,使用NGUI自带的例子<code class="language-plaintext highlighter-rouge">Example 7 - Scroll View (Panel).unity</code>测试下性能吧。把例子中的UIGrid替换为UIBetterGrid,试试复制成百上千个scroll item,跑起来一点也不会卡。</p>
Photoshop Scripting Tutorial
2013-10-10T00:00:00+00:00
http://www.jjcat.com/2013/10/10/Photoshop_Scripting_Tutorial
<p>Photoshop cs2?就开始支持编写脚本,看到美术同学使用Action纪录动作,重放实现批处理,但还是觉得有点麻烦,一来Action中的动作无法改变参数,录制Action又需要手动操作,裁剪,缩放难免可能会引入手动输入错误。所以决定研究下脚本来实现批处理,让美术同学彻底告别录制Action。</p>
<p>要实现的功能非常简单,先缩小原图,然后按中心点裁剪,非常适合我初学Photoshop script。在网上搜索资料,参考资料有</p>
<ul>
<li>
<p>官方的开发文档<a href="http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/photoshop/pdfs/photoshop_cs5_scripting_guide.pdf">Photoshop CS5 Scripting Guide</a></p>
</li>
<li>官方脚本手册<a href="http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/photoshop/pdfs/photoshop_cs5_javascript_ref.pdf">JavaScript</a>,<a href="http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/photoshop/pdfs/photoshop_cs5_applescript_ref.pdf">AppleScript</a>,<a href="http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/photoshop/pdfs/photoshop_cs5_vbscript_ref.pdf">VBScript</a></li>
<li>关于编写UI的,我参考了其中的UI代码<a href="http://www.davidebarranca.com/2012/10/scriptui-window-in-photoshop-palette-vs-dialog/">ScriptUI Window in Photoshop – Palette vs. Dialog</a></li>
</ul>
<p>Photoshop Scripting支持三种脚本语言,JavaScript,AppleScript和VBScript,我选择JavaScript。编写Photoshop 脚本可以使用任何编辑器,但Adobe有自己的脚本编写软件ExtensionScript,有点类似Flash中的脚本编辑器,有代码提示,可以直接运行脚本并进行Debug。</p>
<p>编写好的脚本保存在Photoshop安装目录下的Presents/Scripts 目录下,Photoshop重启后能在菜单->文件->脚本 下找到编写的脚本名字,点击就能运行。</p>
<p>如果要加入GUI那么就要使用ScriptUI,文档在<code class="language-plaintext highlighter-rouge">C:\Program Files (x86)\Adobe\Adobe Utilities - CS6\ExtendScript Toolkit CS6\ExtendScript Toolkit ReadMe.pdf</code>,查看UI一章,还可以参考这篇博客里的代码<a href="http://www.davidebarranca.com/2012/10/scriptui-window-in-photoshop-palette-vs-dialog/">ScriptUI Window in Photoshop – Palette vs. Dialog</a>,对于简单的GUI,使用dialog就可以了。</p>
Commits Recover on Detached Head Mode
2013-10-10T00:00:00+00:00
http://www.jjcat.com/2013/10/10/Commits_Recover_on_Detached_Head_Mode
<p><code class="language-plaintext highlighter-rouge">git checkout <commit></code> 命令会把HEAD设为commit,在这种状态下HEAD没有指向branch, 这种HEAD叫做Detached Head。顾名思义如果在这种情况下提交的话,会在一个不存在branch下工作。但当你切换回master分支或者别的分支,那么在Detached Head下提交的所有内容都不见了。</p>
<p>我在使用source tree的时候就碰到过commit在Detached Head下丢失。在命令行模式下会有警告出现,但使用source tree尽然没出现警告,直接导致我半天的工作找不到了。</p>
<p>接下来说下恢复的方法。查看项目下<code class="language-plaintext highlighter-rouge">.git\objects\</code>目录中的提交记录,使用日期进行排序。然后使用<code class="language-plaintext highlighter-rouge">git show commit</code>查看那些不存在的commit,那些commit就是在Detached Head下提交的,因为不属于任何的分支,所以使用log查看不到。</p>
<p>接下来使用<code class="language-plaintext highlighter-rouge">merge commit</code>合并游离的commit</p>
Type Conver From String
2013-05-09T00:00:00+00:00
http://www.jjcat.com/2013/05/09/Type_Conver_From_String
<p>#Unfinished</p>
<p>我想在Unity中加入一个Console,方便游戏调试,类似上古卷轴,按下slashquote键,跳出输入框,输入命令后执行。</p>
<p>我在Asset商店中找到了一款插件,<a href="http://u3d.as/content/sander-homan/easy-console/3cf">easyconsole</a> 要加25$,我认为可以自己实现一个。</p>
<p>输入命令格式:cmdName parm1 parm2 ….</p>
<p>通过cmdName来执行一个注册函数,函数的参数是parm1 parm2…</p>
<p>很显然参数是字符串,所以要将字符串转换为注册函数的参数类型。</p>
<p>C#的反射功能可以得到函数的参数类型列表,剩下的就是将string转换到相依的参数。</p>
<p>几个要用到的函数:</p>
<ul>
<li>
<p>Type.GetMethor(string funname):MethodInfo 通过注册函数名字获得函数信息</p>
</li>
<li>
<p>MethodInfo.GetParameters():ParameterInfo[] 通过函数信息得到函数参数信息列表</p>
</li>
<li>
<p>MethodInfo.Invoke(Object,Object[]):Object 传入参数列表,调用函数</p>
</li>
</ul>
<p>通过这几个函数就可以动态调用函数了,但问题在于如何将string转为相应的参数类型。</p>
<p>C#中有两种类型转换方法,参考<a href="http://msdn.microsoft.com/en-us/library/yy580hbd.aspx">MSDN</a>,一开始查看IConvertible,发现只能从类转换到值类型,无法从值类型转换到类,而且是不支持反射的。另一种方法是TypeConverter,可以满足我的需要,可以通过借口 Type.ConverFromString(string):Object 来转换到参数类型。</p>
Offset Bezier Curve
2013-03-22T00:00:00+00:00
http://www.jjcat.com/2013/03/22/Offset_Bezier_Curve
<p>#Unfinished</p>
<p>在绘制水流体的过程中我碰到了一个问题,如何利用一条Bezier曲线生成水流体的网格多边形数据。最后我决定先生成一条Bezier曲线,然后对该曲线进行偏移来生成4条Bezier曲线分别代表一个立方体的4条边。对于Bezier的偏移问题,一开始只是简单的想利用平移来实现,但最后出来的效果不竟如人意。在网上搜索后发现这不是一个简单的问题,Bezier的偏移曲线是无法用Bezier曲线来表示的,但有几种近似的解法。</p>
<ul>
<li><a href="http://www.gamedev.net/topic/173228-quotparallelquot-bezier-curves/">GameDev</a>上有关于此问题的一个讨论</li>
<li><a href="http://stackoverflow.com/questions/408457/outline-of-cubic-bezier-curve-stroke">StackOverFlow</a>有人对此提出了问题</li>
<li>论文<a href="http://www.cis.usouthal.edu/~hain/general/Publications/Bezier/BezierFlattening.pdf">Bezier Flattening</a></li>
<li>This <a href="http://seant23.wordpress.com/2010/11/12/offset-bezier-curves/">guy</a> use flash to implement it,他的文章的最后还列出了一些Bezier的参考资料</li>
<li><a href="http://seant23.files.wordpress.com/2010/11/anoffsetalgorithm.pdf">An offset algorithm for polyline curves</a></li>
</ul>
Finishing A Game
2013-03-12T00:00:00+00:00
http://www.jjcat.com/2013/03/12/Finishing_A_Game
<p>作者:<a href="http://www.derekyu.com/">Derek Yu</a></p>
<p>译者:<a href="http://jjcat.me">杰杰猫</a> jjcat@outlook.com</p>
<p><a href="http://makegames.tumblr.com/post/44181247500/making-it-in-indie-games-starter-guide">原文</a>由《安琪拉之歌》的作者<a href="http://www.derekyu.com/">Derek Yu</a>发表在自己的博客中,阅读后感觉收获颇多。<a href="http://article.yeeyan.org/view/62500/351823">译文</a>发表在译言网上,对翻译有任何的建议可以留言,发邮件或在译言上挑错。本人英文水平一般,故有翻译错误还请原谅。下面为译文。</p>
<hr />
<p><img src="http://www.derekyu.com/tumblr/finishgame01.jpg" alt="" /></p>
<p>在我努力完成<a href="http://spelunkyworld.com/">自己游戏</a>期间,我一直在思考一般情况下如何完成项目。我已经注意到有很多优秀的开发人员对于完成游戏这件事都会遇到麻烦。说实话,我身后已经留下了一长串未完成的游戏…我想每个人都有。由于各种原因,不是每个项目都能有成果。但是如果你发现自己对于有潜力的游戏项目也一直在半途而废,那么值得考虑后退一步,检视一下为什么会发生这种情况。</p>
<p>我们至少在一款游戏、漫画、电影等中会有过这样一种感受——“哇,我可以做的比他更好,他被过高评价了”。但退后一步想一想,嗨,他们按时完成了项目,这才是重点,而我却没有。这至少是一件他们做的比我好的事,而这可能就是为什么他们能获得好评的原因。如果你把“完成”看待成一项技能,而不仅仅是过程中的一个步骤,那么你不仅能认识到在这件事上你能做的更好,而且还能获得你自己的习惯和思考方式。</p>
<p>我不认为做游戏存在一种正确的方法。这是一项带有创造性的尝试,所以这里没有一成不变的教条。但是作为一个游戏开发者,一名与其他游戏开发者讨论这个问题的人,我觉得这里存在一些心理上的陷阱,我们在某些时刻都会陷入其中,特别在我们刚开始做的时候。意识到这些陷阱的存在是通往成功完成某事的重要的第一步。(你和我,将这些想法整理成文字在某种程度上是我克服它们的一种手段。)</p>
<p>因此事不宜迟,下面列出15条关于完成游戏的贴士:</p>
<h2 id="1-选择有潜力的想法">1. 选择有潜力的想法</h2>
<p><img src="http://www.derekyu.com/tumblr/finishgame02.jpg" alt="" /></p>
<p>我发现有三种类型的游戏能引起我的兴趣:我想做的游戏,我幻想能做的游戏和我擅长做的游戏。</p>
<p>我想做的游戏是那些本身看起来就很好玩的游戏。可能是它的机制体验起来很有趣,或者是其中有个我特别想刻画的角色。</p>
<p>我幻想能做的游戏是那些我更感兴趣于其结果而不是其制作的过程的游戏。它可能是一款“无限制”概念(“天哪,侠盗猎车手加入最终幻想加入星际争霸加入… ”)的或者只是一个不错的主意,但实现起来并不一定有趣。</p>
<p>我擅长制作的游戏是适合我的个性,我也拥有制作经验的游戏。或许有一种游戏类型是你特别向往的,你能很好的理解它的节奏和流程。</p>
<p>在我看来,富有最大潜力的想法(前提是能完成)都可被划分在这三种类型里,此外还要满足“我有时间和资源制作它”。</p>
<h2 id="2-真正开始这该死的游戏">2. 真正开始这该死的游戏</h2>
<p>写下自己的想法不等于开始那该死的游戏。编写设计文档不等于开始那该的游戏。组建一只团队不等于开始那该死的游戏。甚至连制作画面和声音也不等于开始那该死的游戏。“开始准备那该死的游戏”和“开始那该死的游戏”容易混淆。但只要记住:一款该死的游戏是能玩的。但如果你还没有做出一些可以玩的东西,那就不能称为该死的游戏!</p>
<p>所以,该死,就算制作了一个游戏引擎,对于开始那该死的游戏来说也不是必须的。这点我放到了下一条贴士…</p>
<h2 id="3-不做不必要的自主研发技术">3. 不做不必要的自主研发技术</h2>
<p>编写自己的引擎有利有弊。但问问你自己,真的有必要吗?你现在在做的事情是在现有基础上不能实现的,还是在重新发明轮子?当然,如果编写自己的引擎,你可以在自己的喜好下做的很好。但是,诚实点,你需要多久才能从引擎转到游戏本身呢?你是否发现自己正在制作引擎而不是游戏?</p>
<p>我使用<a href="http://www.yoyogames.com/make">Game Maker</a>制作了《Spelunky》的初始版本,这游戏是一款“完成品”,它最终给予了我制作Xbox360版工作的机会。所以不要觉得游戏制作工具或者其它的一些傻瓜工具是某种不专业的方法。重要的是游戏本身。</p>
<p>相关链接: <a href="http://forums.tigsource.com/index.php?board=4.0">The Independent Gaming Forums Technical Forums</a></p>
<h2 id="4-原型">4. 原型</h2>
<p>这条和第二条相呼应:原型,首先利用你能得到的任何东西。有时候你会马上发现这是一个坏主意。有时候你会偶然发现一个更好的主意。无论哪一种,我通常发现直到我真正开始做之前,都很难决定要选择哪一个。所以做出东西来!</p>
<h2 id="5-确保核心机制是有趣的">5. 确保核心机制是有趣的</h2>
<p>确保围绕核心机制玩起来有趣。仅仅运行最基本的交互应该能表现出好玩,因为那是玩家在玩你游戏过程中一直会操作的部分。最终,你想让该核心机制驱动你的开发。对于你来说,在最后你不得不删除游戏内容的时候会变得容易许多 - 你总能让它回归到核心机制。</p>
<p>完全有可能在原型的制作过程中,你发现了一种比原来更好玩的机制——考虑制作那个新的核心机制!</p>
<h2 id="6-选择好搭档或者坚持一个人工作">6. 选择好搭档(或者坚持一个人工作)</h2>
<p><img src="http://www.derekyu.com/tumblr/finishgame02b.jpg" alt="" /></p>
<p>寻找一名优秀的游戏制作搭档在很多方面就像是约会。你可能认为技能是唯一需要考虑的东西:“哦哇,酷,我是一名程序员,这家伙是一名美术…让我们开始做吧!” 但是别,这里还有其他要考虑的事情,诸如个性,经验,时间和互相的兴趣爱好。像一段浪漫的感情,你肯定不想让你自己或她扮演一个不专注的角色。 使用一些小的项目来测试对方,因为关键人物在开发了几个月或几年离开会变成真正的灾难。</p>
<p>完成项目另外一个重要的事情是你的搭档了解你的能力,你的搭档与你在一起会感到很舒服。很难说服有经验的人独自为一个想法和你一起工作。考虑到能真正被人发现的想法少得可怜(在能运行之前,能察觉到那些想法的价值是多么的困难)。好的搭档想要看到你们完成的游戏。所以完成他们!</p>
<p>另外,在网上寻找可以免费使用的图形和音乐,至少可以拿来做占位符(在The Independent Gaming Source我们有一个<a href="http://www.derekyu.com/tigs/assemblee/">比赛</a>,在上面我们创建了大量免费的美术和音乐)。使用ASCII,如果你一定要的话。作为一名美术,我知道我更喜欢在一个已完成但只缺美术资源的项目上做贡献。如果你需要一名程序员…考虑自己学习编程(如果我能,那么你也可以!)或者挑几款游戏制作的软件(见第三条)。</p>
<h2 id="7-苦差事是正常的----把该因素纳入到你的计划中">7. 苦差事是正常的 - 把该因素纳入到你的计划中</h2>
<p>很多游戏制作是属于单调乏味,彻头彻尾的毫无乐趣的。这不是玩游戏,这是工作(这就是为什么你应该掐死任何一个人,那个人对你说“整天玩游戏”的玩笑话。)。有时候你会突然意识到,在你规划你的项目和原型的时候,这里所有的内容在那时你都没有考虑过-诸如那些菜单,页面切换,存储和读取档案等等。我过去一直在构想这个由我一直创建的令人惊讶的世界,或者是一种我过去体验过的有趣的机制。我没想过我要花费数周的时间来制作功能菜单,以至于它看起来不像坨屎!或者,你知道有些东西在少量的情况下是有趣的,例如制作角色动画,但是当你认识到你要为100个不同的角色制作动画时,这会变的像噩梦一般。</p>
<p>一旦你完成过几次后,你就会意识这些东西对于扩展你的项目是多么的重要,这样你就不会花太多时间在这不可避免的泥潭中(“太多的时间”取决与你何时退出)。你将会意识到这些无聊的东西使得整个游戏变得完整了!一个漂亮的标题页面,就能让游戏变得专业起来。</p>
<h2 id="8-将评奖比赛和其他的活动作为真实的截止时间">8. 将评奖,比赛和其他的活动作为真实的截止时间</h2>
<p>当Alec和我在进行<a href="http://www.bit-blot.com/aquaria/">《安琪拉之歌》</a>的工作时,独立游戏节的提交截止时间迫使我们做出艰难的决定,这些决定包括我们一直讨论的方向,也迫使我们更实际地对待进度表。如果我们没有那个期限,我不能完全肯定我们能完成。参加比赛是重要的,因为期限是现实的,奖励(名气,奖品,可能还有金钱)非常现实。他们也能带给你联系社区里志同道合的人的方法。</p>
<p>相关链接: <a href="http://igf.com/">Independent Games Festival</a>, <a href="http://www.ludumdare.com/">Ludum Dare</a></p>
<h2 id="9-前进">9. 前进</h2>
<p>感觉卡住了?那就继续往下走。开始制作下个关卡,下个敌人,下个某某东西。它不仅有利于处于激励的目的,你也想得到整个游戏展开后的感觉。就像写作——如果在写下一句之前要确保每一句话都是完美,你不会想在这种条件下一句接着一句写。你记下一个大纲。</p>
<h2 id="10-照顾好你的生理和心理健康">10. 照顾好你的生理和心理健康。</h2>
<p><img src="http://www.derekyu.com/tumblr/finishgame03.jpg" alt="" /></p>
<p>当你专注于完成一个游戏时,想照顾好自己是出奇的困难。但说实话,通过不睡觉,不运动,不按时就餐这些方式,你正在对游戏制作造成危害。在最好的情况下,你会阻碍自己的在工作中发挥全部的潜力,让你更容易放弃。对于你的项目有一些疑问是很理所当然的,但持续的沮丧和生病肯定不算。当你的大脑和身体感觉像是一坨废物的时候,你肯定不会想在你梦寐以求的项目上继续工作。</p>
<h2 id="11-不要给重新开始寻找借口">11. 不要给重新开始寻找借口。</h2>
<p><img src="http://www.derekyu.com/tumblr/finishgame04.jpg" alt="" /></p>
<p>“我的代码一团糟。况且我已经学了很多。如果我重新开始,那么我可以做的更快和更好,然后剩下的游戏开发也将会更快!”</p>
<p>STOP,NO。这或多或少是每款游戏开发中真是的情况。您的代码永远是一团糟。你会学到很多东西。它永远能不会完美。如果你从头再来,你会发现自己会重蹈覆辙。能想到这是一个可怕的陷阱。</p>
<p>这里有一个笑话:一个人投入了毕生的时间制作了一款游戏引擎,这款引擎太完美了,完美的游戏仅仅只需按一个按钮就会产生出来。其实,这不算是一个笑话,因为真正的笑点是,他从来没有完成!根本不存在那样的引擎或游戏。</p>
<p>如果糟糕的组织结构真的使你慢下来了,那就回头对它做些手术,这样能让你感觉良好。即使还存在一些hack的方法,如果它能正常运行,那就继续下去!</p>
<h2 id="12-为下一个游戏记录下来">12. 为下一个游戏记录下来</h2>
<p>在开发过程中你会有一个很棒的新想法,它能让所有人都感到惊讶,但你将不得不重做整个游戏去实现它?把它记录下来给下个游戏吧!对吧?但愿现在这个不是你的最后一个游戏。所以记录下来,为下个游戏做准备,先完成现在这个!</p>
<h2 id="13-删除">13. 删除</h2>
<p><img src="http://www.derekyu.com/tumblr/finishgame05.jpg" alt="" /></p>
<p>哦,妈的,你落后于计划表。你拥有的很多想法在你能完成他们一半之前他们都能殖民火星了。哦,你可真不幸啊,但是等等!</p>
<p>嗯,这很棒,没错!因为现在你不得不决定什么才是真正重要内容,对于你的游戏来说,有什么内容你是可以删除。事实上,如果我们有无限的资源和无限的时间,我们会做出同样糟糕,曲解一切的游戏,我们没有任何理由玩它。有限的资源和时间迫使我们制作一款紧凑的游戏,让人们感觉到它是有目的性的。</p>
<p>如果你一直构建一些被证明好玩的核心概念,你只需要一直删减,直到刚好留下那部分核心概念。其它都是一些你可以不用制作的可有可无的内容。更糟糕的是这些可有可无的内容,阻碍了人们发现你游戏里最好的部分。</p>
<h2 id="14-如果没有完成缩小规模而不是扩大">14. 如果没有完成,缩小规模,而不是扩大</h2>
<p>Okay,有时候是该宣布项目停止。可能是因为你永远也没办法完成,一团烂摊子糟糕到得不到任何东西。也许,你的团队已经解散了。我写下这些条目希望帮助人们避免这种可能性,但是,嘿,也许你刚刚关闭了这样一个项目。有时候,你就正好踩到了狗屎。</p>
<p>如果不打算挽回它,至少你要确保缩小下一个项目的规模。把你的眼光抬的越来越高这很容易,即使在你的项目变得越来越不可能完成时。“我的技能提高了!我从失败中吸取了教训,”是一种常见的借口。这就是为什么我认为把完成当成一项技能也是重要的。</p>
<p>(所以,往下走,往下,往下,往下,下到一个点,一个你可能发现它在某种程度上不符合你的身份。例如,不要从4X太空模拟跳到三维4X太空模拟,尝试之一款好游戏,专注于一个小的太空模拟的元素。如果你完成不了它,尝试做一些像Asteroids的游戏。很可能它将会比你预期的要付出更多的努力。(和/或比你预想的做起来更好玩)!</p>
<h2 id="15-最后的10">15. 最后的10%</h2>
<p>有人说最后的10%是真正的90%,这对于游戏制作这完全正确。细节会花费很长的时间。当然,也许你在一个星期内能编写一个完整的战斗系统…但是把它做的更好,做的复杂(没有bug)…这些事可能会花费数月时间。最真实的事实是在你将要做最后一圈冲刺之前你可能会做很多次所谓的“最后一圈”的冲刺。</p>
<p>如果这听起来令人沮丧,但它本不应该啊。虽然最后的10%是悲惨的,但我还是发现在开发中这是一段非常让人满意的时间。因为经常是这样的,如果你已经正确的花费时间,那么那些东西真的在最后看上去像是结合在一起的,把一堆杂乱的想法和内容变成一个美味的游戏点心是一种神奇的感觉。</p>
<p>这就是所有的细节。</p>
<h2 id="终于发布了">终于…发布了!</h2>
<p><img src="http://www.derekyu.com/tumblr/finishgame06.jpg" alt="" /></p>
<p>靠,你发布了一款游戏!恭喜你,你升级了,这是一个重要的时刻。获得的奖励有:信心的提升,有能力完成项目的名声,理解游戏制作的全过程!不过最重要的是,你有一个美妙的小游戏,我可以享受!同制作游戏一样,我也真的很喜欢玩游戏。</p>
<p><strong>朋友,别再束手旁观了:你是一名游戏开发者。</strong></p>
Unexpected Love
2013-01-29T00:00:00+00:00
http://www.jjcat.com/2013/01/29/GGJ_2013_Released
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Unity Web Player | Unexpected Love</title>
<script type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js'></script>
<script type="text/javascript">
<!--
var unityObjectUrl = "http://webplayer.unity3d.com/download_webplayer-3.x/3.0/uo/UnityObject2.js";
if (document.location.protocol == 'https:')
unityObjectUrl = unityObjectUrl.replace("http://", "https://ssl-");
document.write('<script type="text\/javascript" src="' + unityObjectUrl + '"><\/script>');
-->
</script>
<script type="text/javascript">
<!--
var config = {
width: 960,
height: 600,
params: { enableDebugging:"0" }
};
var u = new UnityObject2(config);
jQuery(function() {
var $missingScreen = jQuery("#unityPlayer").find(".missing");
var $brokenScreen = jQuery("#unityPlayer").find(".broken");
$missingScreen.hide();
$brokenScreen.hide();
u.observeProgress(function (progress) {
switch(progress.pluginStatus) {
case "broken":
$brokenScreen.find("a").click(function (e) {
e.stopPropagation();
e.preventDefault();
u.installPlugin();
return false;
});
$brokenScreen.show();
break;
case "missing":
$missingScreen.find("a").click(function (e) {
e.stopPropagation();
e.preventDefault();
u.installPlugin();
return false;
});
$missingScreen.show();
break;
case "installed":
$missingScreen.remove();
break;
case "first":
break;
}
});
u.initPlugin(jQuery("#unityPlayer")[0], "Web.unity3d");
});
-->
</script>
<style type="text/css">
<!--
body {
font-family: Helvetica, Verdana, Arial, sans-serif;
background-color: white;
color: black;
text-align: center;
}
a:link, a:visited {
color: #000;
}
a:active, a:hover {
color: #666;
}
p.header {
font-size: small;
}
p.header span {
font-weight: bold;
}
p.footer {
font-size: x-small;
}
div.content {
margin: auto;
width: 960px;
}
div.broken,
div.missing {
margin: auto;
position: relative;
top: 50%;
width: 193px;
}
div.broken a,
div.missing a {
height: 63px;
position: relative;
top: -31px;
}
div.broken img,
div.missing img {
border-width: 0px;
}
div.broken {
display: none;
}
div#unityPlayer {
cursor: default;
height: 600px;
width: 960px;
}
-->
</style>
</head>
<body>
<p class="header"><span>Unity Web Player | </span>Unexpected Love</p>
<div class="content">
<div id="unityPlayer">
<div class="missing">
<a href="http://unity3d.com/webplayer/" title="Unity Web Player. Install now!">
<img alt="Unity Web Player. Install now!" src="http://webplayer.unity3d.com/installation/getunity.png" width="193" height="63" />
</a>
</div>
<div class="broken">
<a href="http://unity3d.com/webplayer/" title="Unity Web Player. Install now! Restart your browser after install.">
<img alt="Unity Web Player. Install now! Restart your browser after install." src="http://webplayer.unity3d.com/installation/getunityrestart.png" width="193" height="63" />
</a>
</div>
</div>
</div>
<p class="footer">« created with <a href="http://unity3d.com/unity/" title="Go to unity3d.com">Unity</a> »</p>
</body>
</html>
Our Game on GGJ 2013
2013-01-27T00:00:00+00:00
http://www.jjcat.com/2013/01/27/GGJ_2013
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Unity Web Player | Unexpected Love</title>
<script type='text/javascript' src='https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js'></script>
<script type="text/javascript">
<!--
var unityObjectUrl = "http://webplayer.unity3d.com/download_webplayer-3.x/3.0/uo/UnityObject2.js";
if (document.location.protocol == 'https:')
unityObjectUrl = unityObjectUrl.replace("http://", "https://ssl-");
document.write('<script type="text\/javascript" src="' + unityObjectUrl + '"><\/script>');
-->
</script>
<script type="text/javascript">
<!--
var config = {
width: 960,
height: 600,
params: { enableDebugging:"0" }
};
var u = new UnityObject2(config);
jQuery(function() {
var $missingScreen = jQuery("#unityPlayer").find(".missing");
var $brokenScreen = jQuery("#unityPlayer").find(".broken");
$missingScreen.hide();
$brokenScreen.hide();
u.observeProgress(function (progress) {
switch(progress.pluginStatus) {
case "broken":
$brokenScreen.find("a").click(function (e) {
e.stopPropagation();
e.preventDefault();
u.installPlugin();
return false;
});
$brokenScreen.show();
break;
case "missing":
$missingScreen.find("a").click(function (e) {
e.stopPropagation();
e.preventDefault();
u.installPlugin();
return false;
});
$missingScreen.show();
break;
case "installed":
$missingScreen.remove();
break;
case "first":
break;
}
u.initPlugin(jQuery("#unityPlayer")[0], "UnexpedtedLove.unity3d");
});
-->
</script>
<style type="text/css">
<!--
body {
font-family: Helvetica, Verdana, Arial, sans-serif;
background-color: white;
color: black;
text-align: center;
}
a:link, a:visited {
color: #000;
}
a:active, a:hover {
color: #666;
}
p.header {
font-size: small;
}
p.header span {
font-weight: bold;
}
p.footer {
font-size: x-small;
}
div.content {
margin: auto;
width: 960px;
}
div.broken,
div.missing {
margin: auto;
position: relative;
top: 50%;
width: 193px;
}
div.broken a,
div.missing a {
height: 63px;
position: relative;
top: -31px;
}
div.broken img,
div.missing img {
border-width: 0px;
}
div.broken {
display: none;
}
div#unityPlayer {
cursor: default;
height: 600px;
width: 960px;
}
-->
</style>
</head>
<body>
<p class="header"><span>Unity Web Player | </span>Unexpected Love</p>
<div class="content">
<div id="unityPlayer">
<div class="missing">
<a href="http://unity3d.com/webplayer/" title="Unity Web Player. Install now!">
<img alt="Unity Web Player. Install now!" src="http://webplayer.unity3d.com/installation/getunity.png" width="193" height="63" />
</a>
</div>
<div class="broken">
<a href="http://unity3d.com/webplayer/" title="Unity Web Player. Install now! Restart your browser after install.">
<img alt="Unity Web Player. Install now! Restart your browser after install." src="http://webplayer.unity3d.com/installation/getunityrestart.png" width="193" height="63" />
</a>
</div>
</div>
</div>
<p class="footer">« created with <a href="http://unity3d.com/unity/" title="Go to unity3d.com">Unity</a> »</p>
</body>
</html>
编程工具推荐
2013-01-09T00:00:00+00:00
http://www.jjcat.com/2013/01/09/编程工具推荐
<p>##Atomineer</p>
<p>一款Visual Studio的自动化注释插件。强大的地方是可以根据你的函数的名字自动联系生成注释内容,没错!注释风格可以自定义,生成的注释可以被文档化。下面的注释都是自动生成的,我没有更改过,可以看到他可以节省你很多时间写这些机械的注释内容。<br />
下载地址:<a href="http://www.atomineerutils.com/download.php">http://www.atomineerutils.com/download.php</a></p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="cm">/**********************************************************************************************//**
* @fn void Start ()
*
* @brief Starts this object.
**************************************************************************************************/</span>
<span class="k">void</span> <span class="nf">Start</span> <span class="p">()</span> <span class="p">{</span>
<span class="p">}</span>
<span class="cm">/**********************************************************************************************//**
* @fn void Update ()
*
* @brief Updates this object.
**************************************************************************************************/</span>
<span class="k">void</span> <span class="nf">Update</span> <span class="p">()</span> <span class="p">{</span>
<span class="p">}</span>
<span class="cm">/**********************************************************************************************//**
* @fn GameObject FindObject(int index)
*
* @brief Searches for the first object.
*
* @param index Zero-based index of the.
*
* @return The found object.
**************************************************************************************************/</span>
<span class="n">GameObject</span> <span class="nf">FindObject</span><span class="p">(</span><span class="kt">int</span> <span class="n">index</span><span class="p">)</span>
<span class="p">{</span>
<span class="p">}</span></code></pre></figure>
<p>##Productivity Power Tools</p>
<p>微软官方的Visual Studio插件。增加了文本编辑器的当前行高亮,增强了标签功能,标签可以显示不同的颜色区分不同的工程。还增加了滑动条功能,滑动条可以显示代码缩略图,错误位置。还有其它一些功能参见下载地址<br />
下载地址:<a href="http://visualstudiogallery.msdn.microsoft.com/d0d33361-18e2-46c0-8ff2-4adea1e34fef">http://visualstudiogallery.msdn.microsoft.com/d0d33361-18e2-46c0-8ff2-4adea1e34fef</a></p>
<p><img src="\image\Others\Visual_Stuido.png" alt="" /></p>
<p>##jude-community
免费的UML绘图工具,不过现在已经不更新了。小巧快速简单是它的特点。<br />
下载地址:<a href="http://ishare.iask.sina.com.cn/f/4937339.html">http://ishare.iask.sina.com.cn/f/4937339.html</a></p>
<p><img src="\image\Others\jude-community.png" alt="" /></p>
<p>##CLCL
window下的多重剪贴板。文本,文件都支持。<br />
下载地址:<a href="http://www.nakka.com/soft/clcl/index_eng.html">http://www.nakka.com/soft/clcl/index_eng.html</a></p>
<p><img src="\image\Others\CLCL.png" alt="" /></p>
<p>##Everything
windows下的文件搜索软件,速度真的很快,瞬间就完成搜索了。用了之后再也不会想用自带的搜索功能了。<br />
下载地址:<a href="http://www.voidtools.com/download.php">http://www.voidtools.com/download.php</a></p>
<p><img src="\image\Others\Everything.png" alt="" /></p>
2D Water
2013-01-06T00:00:00+00:00
http://www.jjcat.com/2013/01/06/2D_Water
<p>参考了<a href="http://gamedev.tutsplus.com/tutorials/implementation/make-a-splash-with-2d-water-effects/">这篇</a>文章,使用Flash实现。
<a href="https://github.com/jjcat/2D_Water_Demo">下载</a>源文件</p>
<p>点击蓝色区域可以激起水波,右侧控制面板可以调节水波的参数。</p>
<embed src="/assets/2DWater.swf" type="application/x-shockwave-flash" width="550" height="400" />
<p></embed></p>
厕所大战设计文档
2012-12-28T00:00:00+00:00
http://www.jjcat.com/2012/12/28/GDD
<p>##概要</p>
<p>厕所大战是一款休闲联机游戏,是一款很男人的游戏哦~<del>>_«br>
支持2-4人联机游戏,游戏平台暂定为PC(我是很想坐iphone,winphone,不过我没设备测试啊</del>)</p>
<p>##背景</p>
<p>男人之间的战斗!当你解开腰带,掏出鸡鸡已经万事俱备之时,突然从旁边冒出了1..2..3个男人,他们也已经整装待发,一场男人之间的对决正拉开帷幕。</p>
<p>##机制</p>
<p>###核心玩法</p>
<p>玩家控制尿液射出的方向,‘清洁’小便池表面的污垢。每清理完一块污垢能得到相应的分数。时间结束(膀胱放空)后看谁的分数最多。</p>
<p>##游戏流程</p>
<p>###主菜单</p>
<p>主菜单只有两个选项:</p>
<ul>
<li>
<p>创建游戏:
点击创建游戏选项,会弹出一个输入框,输入大厅名字,当玩家输入完后,单击确定,则场景会跳转到大厅场景。</p>
</li>
<li>
<p>加入游戏:
点击加入游戏,如果当前局域网中有大厅,则弹出大厅列表,玩家从中选择一个,最后场景跳转到大厅。如果当前没有建立大厅,则弹出提示告知玩家当前还没有大厅。</p>
</li>
</ul>
<p>####创建游戏</p>
<p>####加入游戏</p>
<p>##游戏元素</p>
<p>###玩家</p>
<p>总共有4名形象不同的玩家,prototype阶段只实现一个角色形象。角色除了形象不同外能力没有区别。</p>
<table class="table-striped">
<thead>
<tr>
<th>玩家姓名</th>
<th>外形</th>
</tr>
</thead>
<tbody>
<tr>
<td>王JJ(本人)</td>
<td>满脸胡渣的大叔,身体矮胖型</td>
</tr>
<tr>
<td>杨伟</td>
<td>皮包骨头的那种瘦子</td>
</tr>
<tr>
<td>杨局长</td>
<td>肚子很大拉,小便的时候看不到鸡鸡的那种身材</td>
</tr>
<tr>
<td>史真香</td>
<td>人妖,有眼影</td>
</tr>
</tbody>
</table>
<p>###污垢</p>
<p>玩家清楚污垢可以获得奖励,便池表面的污垢每次游戏一开始随机形成。,污垢才会消失,如果中间中断,那么前面的时间作废。</p>
<p>游戏中有两种污垢:</p>
<ul>
<li>
<p>尿渍
尿渍是黏附在便池表面的污垢,它不会移动,玩家必须把尿液对准污尿渍射击N秒钟后,尿渍才会被清除。中间如果没有瞄准而导致中断,则时间会重新计算。</p>
</li>
<li>
<p>便渍
便渍是会移动的污垢,当被玩家的尿液击中后他会移动,玩家必须将其射击到下水道口才算清除,才会获得奖励。</p>
</li>
</ul>
<p>每种污垢清除奖励:</p>
<table class="table-striped">
<thead>
<tr>
<th>污垢</th>
<th>奖励</th>
</tr>
</thead>
<tbody>
<tr>
<td>尿渍(小)</td>
<td>10</td>
</tr>
<tr>
<td>尿渍(中)</td>
<td>30</td>
</tr>
<tr>
<td>尿渍(大)</td>
<td>60</td>
</tr>
<tr>
<td>便渍(小)</td>
<td>30</td>
</tr>
<tr>
<td>便渍(中)</td>
<td>60</td>
</tr>
<tr>
<td>便渍(大)</td>
<td>90</td>
</tr>
</tbody>
</table>
<p>尿渍属性表:</p>
<table class="table-striped">
<thead>
<tr>
<th>种类</th>
<th>清除时间</th>
</tr>
</thead>
<tbody>
<tr>
<td>尿渍小</td>
<td>3秒</td>
</tr>
<tr>
<td>尿渍中</td>
<td>6秒</td>
</tr>
<tr>
<td>尿渍大</td>
<td>9秒</td>
</tr>
</tbody>
</table>
<p>便渍属性表:</p>
<table class="table-striped">
<thead>
<tr>
<th>种类</th>
<th>移动速度</th>
</tr>
</thead>
<tbody>
<tr>
<td>便渍小</td>
<td>小</td>
</tr>
<tr>
<td>便渍中</td>
<td>中</td>
</tr>
<tr>
<td>便渍大</td>
<td>大</td>
</tr>
</tbody>
</table>
<p>###其它污垢</p>
<p>####苍蝇</p>
<p>苍蝇会在游戏过程中随机的出现在屏幕中,苍蝇会从屏幕外飞进,然后飞出,如果玩家能射中苍蝇将得到额外的bounce100分。</p>
Unity3D Tools Lesson 1
2012-12-15T00:00:00+00:00
http://www.jjcat.com/2012/12/15/Unity3D制作编辑器工具教程_1——Inspector
<p>#Unity3D Tools Lesson 1 {: .hide-text}</p>
<ul id="markdown-toc">
<li><a href="#简介" id="markdown-toc-简介">简介</a></li>
<li><a href="#初始化" id="markdown-toc-初始化">初始化</a></li>
</ul>
<h2 id="简介">简介</h2>
<p>Inspector面板可以用来对Component和Asset进行快速编辑。如果您的Unity中没有看到Inspector面板,可以通过快捷键方式<em>Ctrl+3</em>打开。这节课我们将对一个类制作一个自定义的Inspector面板。</p>
<p>##默认的Inspector样式##
新建一个C#文件,命名为<strong>MyPlayer.cs</strong>,输入下面的代码。这些代码定义了一个MyPlayer class,它继承自MonoBehaviour,是一个用户自定义的component。</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">using</span> <span class="nn">UnityEngine</span><span class="p">;</span>
<span class="p">[</span><span class="n">System</span><span class="p">.</span><span class="n">Serializable</span><span class="p">]</span>
<span class="k">using</span> <span class="nn">System.Collections</span><span class="p">;</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">MyPlayer</span> <span class="p">:</span> <span class="n">MonoBehaviour</span>
<span class="p">{</span>
<span class="k">public</span> <span class="kt">int</span> <span class="n">armor</span> <span class="p">=</span> <span class="m">75</span><span class="p">;</span>
<span class="k">public</span> <span class="kt">int</span> <span class="n">damage</span> <span class="p">=</span> <span class="m">75</span><span class="p">;</span>
<span class="k">public</span> <span class="n">GameObject</span> <span class="n">gun</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>
<blockquote class="alert">
<dl>
<dt>注意</dt>
<dd>是不是只有派生自MonoBehaviour的类才能够进行自定义化Inspector? 不是,例如贴图,模型等不用附加到GameObject上的资源文件,可以在Project面板下显示并选中,在Inspecotor面板中会列出这些资源的属性,并且可以进行修改。如果您也要自定义asset,需要继承自ScriptableObject class。您可以在Project面板中选择一个FBX文件,或者一个Texture文件,查看Inspector的变化。自定义资源和Inspector会在以后的课程中进行讲解。</dd>
</dl>
</blockquote>
<p>然后我们在场景中新建一个GameObject,重命名为MyPlayer,将上面的脚本添加到该GameObject上。
如果一个GameObject拥有该component,那么在Inspector面板中会显示相关的一些信息。默认情况下,Unity会自动为我们生成一个component编辑面板,他会把所有的public的变量显示在Inspector中。如下图:</p>
<p><a href="http://imgur.com/piiO0.png"><img src="http://imgur.com/piiO0.png" alt="" /></a>
######默认的Inspector面板</p>
<p>##自定义Inspector面板##
Unity默认的Inspector面板可以随意对该变量进行修改。如果MyPlayer类中的这些变量对输入的数值有范围要求,例如只能输入0到100,这在默认的输入框下是无法做到的。但在自定义Inspector中,我们可以使用slider bar控件对输入范围进行限制。如果这个MyPlayer最后是要给关卡设计师使用,他就不可能输入一些无效的数值。下图为自定义后的Inspector,可以看到我们用slider bar代替了原来的输入框,并且设置slider bar的输入数值的范围为0到100,还加入了一个progress bar显示数值的范围。通过自定义Inspector就能制作适合各种类型的component的Inspector。</p>
<p><a href="http://i.imgur.com/h03dr.png"><img src="http://i.imgur.com/h03dr.png" alt="" /></a>
######自定义的Inspector面板</p>
<p>现在我们开始自定义Inspector面板的内容,自定义后的Inspector如上图所示。
首先创建一个新的C#文件,命名为<strong>MyPlayerEditor.cs</strong>,并将该文件放在<strong>Editor</strong>文件夹下。</p>
<p><a href="http://i.imgur.com/lxcx6.png"><img src="http://i.imgur.com/lxcx6.png" alt="" /></a>
######创建MyPlayerEditor.cs</p>
<blockquote class="alert">
<p>注意
:为什么要放在Editor文件夹下?Unity规定所有的Editor classes都必须放在Editor文件夹下,这样Untiy运行Editor文件夹的Editor class,游戏中运行的代码不要放在Editor文件夹下。</p>
</blockquote>
<p>下面的代码将会创建一个自定义的Inspector,你将会在Inspector面板中看到最后的效果。</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">using</span> <span class="nn">UnityEngine</span><span class="p">;</span>
<span class="k">using</span> <span class="nn">UnityEditor</span><span class="p">;</span>
<span class="p">[</span><span class="nf">CustomEditor</span><span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="n">MyPlayer</span><span class="p">))]</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">MyPlayerInspector</span> <span class="p">:</span> <span class="n">Editor</span>
<span class="p">{</span>
<span class="k">public</span> <span class="kt">int</span> <span class="n">damageProp</span><span class="p">;</span>
<span class="k">public</span> <span class="kt">int</span> <span class="n">armorProp</span><span class="p">;</span>
<span class="k">public</span> <span class="n">GameObject</span> <span class="n">gunProp</span><span class="p">;</span>
<span class="c1">// Initilization
</span>
<span class="k">void</span> <span class="nf">OnEnable</span><span class="p">()</span>
<span class="p">{</span>
<span class="n">MyPlayer</span> <span class="n">myPlayer</span> <span class="p">=</span> <span class="n">target</span> <span class="k">as</span> <span class="n">MyPlayer</span><span class="p">;</span>
<span class="n">damageProp</span> <span class="p">=</span> <span class="n">myPlayer</span><span class="p">.</span><span class="n">damage</span><span class="p">;</span>
<span class="n">armorProp</span> <span class="p">=</span> <span class="n">myPlayer</span><span class="p">.</span><span class="n">armor</span><span class="p">;</span>
<span class="n">gunProp</span> <span class="p">=</span> <span class="n">myPlayer</span><span class="p">.</span><span class="n">gun</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">OnInspectorGUI</span><span class="p">()</span>
<span class="p">{</span>
<span class="c1">// show slider and progress bar
</span>
<span class="n">damageProp</span> <span class="p">=</span> <span class="n">EditorGUILayout</span><span class="p">.</span><span class="nf">IntSlider</span><span class="p">(</span><span class="s">"Damage"</span><span class="p">,</span> <span class="n">damageProp</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">100</span><span class="p">);</span>
<span class="nf">ProgressBar</span><span class="p">(</span><span class="n">damageProp</span><span class="p">/</span> <span class="m">100.0f</span><span class="p">,</span> <span class="s">"Damage"</span><span class="p">);</span>
<span class="c1">// show slider and progress bar
</span>
<span class="n">armorProp</span> <span class="p">=</span> <span class="n">EditorGUILayout</span><span class="p">.</span><span class="nf">IntSlider</span><span class="p">(</span><span class="s">"Armor"</span><span class="p">,</span> <span class="n">armorProp</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">100</span><span class="p">);</span>
<span class="nf">ProgressBar</span><span class="p">(</span><span class="n">armorProp</span><span class="p">/</span> <span class="m">100.0f</span><span class="p">,</span> <span class="s">"Armor"</span><span class="p">);</span>
<span class="n">gunProp</span> <span class="p">=</span> <span class="n">EditorGUILayout</span><span class="p">.</span><span class="nf">ObjectField</span><span class="p">(</span><span class="s">"Player's Gun"</span><span class="p">,</span> <span class="n">gunProp</span><span class="p">,</span> <span class="k">typeof</span><span class="p">(</span><span class="n">GameObject</span><span class="p">),</span> <span class="k">true</span><span class="p">)</span> <span class="k">as</span> <span class="n">GameObject</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Custom GUILayout progress bar.
</span>
<span class="k">private</span> <span class="k">void</span> <span class="nf">ProgressBar</span><span class="p">(</span><span class="kt">float</span> <span class="k">value</span><span class="p">,</span> <span class="kt">string</span> <span class="n">label</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">Rect</span> <span class="n">rect</span> <span class="p">=</span> <span class="n">GUILayoutUtility</span><span class="p">.</span><span class="nf">GetRect</span><span class="p">(</span><span class="m">18</span><span class="p">,</span> <span class="m">18</span><span class="p">,</span> <span class="s">"TextField"</span><span class="p">);</span>
<span class="n">EditorGUI</span><span class="p">.</span><span class="nf">ProgressBar</span><span class="p">(</span><span class="n">rect</span><span class="p">,</span> <span class="k">value</span><span class="p">,</span> <span class="n">label</span><span class="p">);</span>
<span class="n">EditorGUILayout</span><span class="p">.</span><span class="nf">Space</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span></code></pre></figure>
<p>然后打开Unity编辑器,选中GameObject,您就能看到自定义Inspector面板的内容了。下面对这MyPlayerEditor类进行详细讲解。</p>
<p>##Editor class##
Editor class是所有自定义Inspector的基类。我们必需从Editor class开始派生我们自己的Inspector。</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="p">[</span><span class="nf">CustomEditor</span><span class="p">(</span><span class="k">typeof</span><span class="p">(</span><span class="n">MyPlayer</span><span class="p">))]</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">MyPlayerInspector</span> <span class="p">:</span> <span class="n">Editor</span>
<span class="p">{</span>
<span class="c1">// others...
</span>
<span class="p">}</span></code></pre></figure>
<p>下面这行代码指定了自定义Inspector的关联类的类型,我们这里要对MyPlayer类进行关联。这行代码的作用就是当您选中含有MyPlayer的GameObject时,就会显示MyPlayerInspector。</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="p">[</span><span class="n">CustomEditor</span><span class="p">[</span><span class="k">typeof</span><span class="p">(</span><span class="n">MyPlayer</span><span class="p">)]</span></code></pre></figure>
<h2 id="初始化">初始化</h2>
<p>Editor class继承自ScriptableObject,ScriptableObject有三个关于生命周期的消息响应函数:</p>
<ul>
<li>OnEable</li>
<li>OnDisable</li>
<li>OnDestory</li>
</ul>
<p>对于MyPlayerInspector来说,每当要显示自定义Inspector的时候就会调用OnEable,当切换到其他Inspector面板的时候就会调用OnDisable。所以OnEnable是进行初始化最好的地方。</p>
<p>在下述的初始化代码中,我们获取了当前选中的对象的成员变量。</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">void</span> <span class="nf">OnEnable</span><span class="p">()</span>
<span class="p">{</span>
<span class="n">MyPlayer</span> <span class="n">myPlayer</span> <span class="p">=</span> <span class="n">target</span> <span class="k">as</span> <span class="n">MyPlayer</span><span class="p">;</span>
<span class="n">damageProp</span> <span class="p">=</span> <span class="n">myPlayer</span><span class="p">.</span><span class="n">damage</span><span class="p">;</span>
<span class="n">armorProp</span> <span class="p">=</span> <span class="n">myPlayer</span><span class="p">.</span><span class="n">armor</span><span class="p">;</span>
<span class="n">gunProp</span> <span class="p">=</span> <span class="n">myPlayer</span><span class="p">.</span><span class="n">gun</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>
<p>在上述的代码中,出现了一个新变量target,他代表了当前要进行Inspect的对象,在MyPlayerEditor类中,我们指定Inspect MyPlayer类,所以target就是当前My Player的一个引用。我们使用as操作符先对target进行类型转化。</p>
<p>Edtior类还有两个成员变量是用来表示当前选中的对象,他们分别叫做targets和serializedObject。</p>
<p>##绘制GUI##
如果您在游戏开发中使用过Unity自带的GUI类,那么您会发现EditorGUI类接口同GUI基本是同样的设计思路。所有的UI绘制必须放在OnInspectorGUI方法中,就好比在游戏中所与的GUI函数必须放在OnGUI方法中。</p>
<figure class="highlight"><pre><code class="language-csharp" data-lang="csharp"><span class="k">public</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">OnInspectorGUI</span><span class="p">()</span>
<span class="p">{</span>
<span class="c1">// show slider and progress bar
</span>
<span class="n">damageProp</span> <span class="p">=</span> <span class="n">EditorGUILayout</span><span class="p">.</span><span class="nf">IntSlider</span><span class="p">(</span><span class="s">"Damage"</span><span class="p">,</span> <span class="n">damageProp</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">100</span><span class="p">);</span>
<span class="nf">ProgressBar</span><span class="p">(</span><span class="n">damageProp</span><span class="p">/</span> <span class="m">100.0f</span><span class="p">,</span> <span class="s">"Damage"</span><span class="p">);</span>
<span class="c1">// show slider and progress bar
</span>
<span class="n">armorProp</span> <span class="p">=</span> <span class="n">EditorGUILayout</span><span class="p">.</span><span class="nf">IntSlider</span><span class="p">(</span><span class="s">"Armor"</span><span class="p">,</span> <span class="n">armorProp</span><span class="p">,</span> <span class="m">0</span><span class="p">,</span> <span class="m">100</span><span class="p">);</span>
<span class="nf">ProgressBar</span><span class="p">(</span><span class="n">armorProp</span><span class="p">/</span> <span class="m">100.0f</span><span class="p">,</span> <span class="s">"Armor"</span><span class="p">);</span>
<span class="n">gunProp</span> <span class="p">=</span> <span class="n">EditorGUILayout</span><span class="p">.</span><span class="nf">ObjectField</span><span class="p">(</span><span class="s">"Player's Gun"</span><span class="p">,</span> <span class="n">gunProp</span><span class="p">,</span> <span class="k">typeof</span><span class="p">(</span><span class="n">GameObject</span><span class="p">),</span> <span class="k">true</span><span class="p">)</span> <span class="k">as</span> <span class="n">GameObject</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Custom GUILayout progress bar.
</span>
<span class="k">private</span> <span class="k">void</span> <span class="nf">ProgressBar</span><span class="p">(</span><span class="kt">float</span> <span class="k">value</span><span class="p">,</span> <span class="kt">string</span> <span class="n">label</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">Rect</span> <span class="n">rect</span> <span class="p">=</span> <span class="n">GUILayoutUtility</span><span class="p">.</span><span class="nf">GetRect</span><span class="p">(</span><span class="m">18</span><span class="p">,</span> <span class="m">18</span><span class="p">,</span> <span class="s">"TextField"</span><span class="p">);</span>
<span class="n">EditorGUI</span><span class="p">.</span><span class="nf">ProgressBar</span><span class="p">(</span><span class="n">rect</span><span class="p">,</span> <span class="k">value</span><span class="p">,</span> <span class="n">label</span><span class="p">);</span>
<span class="n">EditorGUILayout</span><span class="p">.</span><span class="nf">Space</span><span class="p">();</span>
<span class="p">}</span></code></pre></figure>
<p>EditorGUI类有一个方便布局的版本,叫做EditorGUILayout。使用EdiorGUILayout可以不用指定每个控件的坐标位置从而省去了每次进行计算坐标的苦差事。EditorGUILayout会更具调用顺序进行自动布局。在上述代码中我就使用了EditorGUILayout创建progress bar和slider bar。</p>
<p>##总结##
我们在这一课中详细介绍了创建一个自定义Inspector的详细步骤。我们制作了一个简单的Inspector面板,下一课我们会具体介绍更多的Editor class的功能,重新制作一个纹理查看器。</p>
Unity3D Tools Lesson 0
2012-12-15T00:00:00+00:00
http://www.jjcat.com/2012/12/15/Unity3D制作编辑器工具教程_0——概述
<p>#Unity3D Tools Lesson 0 {: .hide-text}</p>
<ul id="markdown-toc">
<li><a href="#在unity中创建自己的工具编辑器" id="markdown-toc-在unity中创建自己的工具编辑器">在Unity中创建自己的工具编辑器</a></li>
</ul>
<p>##简介
使用Unity提供的Editor类库,可以让开发者创建自定义的Inspector面板和窗口。<a href="http://docs.unity3d.com/Documentation/ScriptReference/20_class_hierarchy.Editor_Classes.html">这里</a>是Unity官方的Editor类库在线参考文档,您在接下来的学习和开发中将会一直使用它查找相关类参考文档。</p>
<p>##环境配置 {#h2name}
安装最新的Unity4,您可以从<a href="http://untiy3d.com/#unity4" title="Unity offical website">Unity官方</a>网站上下载。Unity4的普通版是免费的:)<br />
安装Mono Develop,您可以用Mono Develop进行调试。Unity4会默认安装Mono Develop。<br />
你也可以使用自己喜欢的文本编辑器,我喜欢Visual Studio,但是别的软件不能进行调试。</p>
<p>##Editor Classes
在Unity的官方Scripting Reference的目录下,他把所有的Scripting分为两类,一类叫做Runtime Classes,还有一类叫做Editor Classes。如果您只是用Unity进行游戏开发,那么您可能只接触过Runtime Classes,Runtime Classes是在游戏中运行的Scripting。而Editor Classes是在Unity下运行的Scripting。如果我们要制作工具,那么一定是在Unity下运行的,所以这些就是进行工具制作的类库。</p>
<p>Editor Classes指代Unity中的Editor类库,它包含了几十个关于Unity编辑器的类。这些类的功能很多,能帮助您在Unity下创建自己的工具。例如有关于Asset的类,他们能让您创建,修改,删除Asset,这样您可以自定义资源,并导入到Unity中进行编辑和修改。还有强大的动画工具类,可以创建和访问动画文件,如果你觉得Unity自带的Animation Editor还不能满足您的要求,你完全可以自己制作一个新的Animation Editor进行动画的创建和修改。</p>
<p>##自定义Inspector面板和默认Inspector面板的对比
下图中,左边的Inspector是Unity默认给My Player类创建的,右边的Inspector面板是我们自定义的。显然右边的Inspector面板更加适合设计师用来进行数值的修改,它包含了最大和最小值的区间,并用一个Loading Bar来展现视觉要素。
<a href="http://i.imgur.com/0Qc0M.png"><img src="http://i.imgur.com/0Qc0M.png" alt="" /></a>
<small class="muted">默认的Inspector和自定义Inspector的对比</small></p>
<h2 id="在unity中创建自己的工具编辑器">在Unity中创建自己的工具编辑器</h2>
<p>下图中左侧的窗口是用户自定义窗口,他可以像其它的Unity窗口那样进行布局和摆放。</p>
<p><a href="http://i.imgur.com/6TaK1.jpg"><img src="http://i.imgur.com/6TaK1.jpg" alt="" /> </a>
<small class="muted">一个Unity Reference中的窗口例子</small></p>
<p>##不是C#用户
本教程采用C#语言编写,如果您从来都没有在Unity中使用过C#,那么请先阅读官方文档<em>Overview: Writing Scripts in C# & Boo</em>(<a href="http://docs.unity3d.com/Documentation/ScriptReference/index.Writing_Scripts_in_Csharp_26_Boo.html">英文官方</a>,<a href="http://game.ceeger.com/Script/Overview/Overview.Writing_Scripts_in_C.html">中文翻译</a>)。
对于Javascript和Boo用户,<a href="http://answers.unity3d.com/questions/12911/what-are-the-syntax-differences-in-c-and-javascrip.html">这篇</a>是关于Javascript和C#语言不同之处的比较。<a href="http://boo.codehaus.org/Differences+with+Csharp">这篇</a>是关于C#和Boo语言不同之处的比较。这两篇文章可以很快速的让你了解C#的语法,让您能以最短的学习时间读懂接下去的C#代码。</p>
<p>##调试
如果想要设置断点单步执行程序,那么必须使用Unity自带的IDE Mono Develop。想要调试Editor class的代码,首先得把Unity Debugger attach 上Unity Editor。步骤如下:启动Mono Develop,点击菜单栏<strong>Run</strong>-><strong>Attach to Process…</strong>,选中Unity Editor并点击Attach。完成这步后,就可以进行单步调试了。
<a href="http://i.imgur.com/TKAop.png"><img src="http://i.imgur.com/TKAop.png" alt="" /></a>
<small class="muted">Mono Develop Attach to Process…对话框</small></p>
<blockquote class="alert">
<p>注意
:(如果出现Attaching失败,尝试查看Unity的菜单<strong>Edit</strong>-><strong>Preferences…</strong>点击<strong>External Tools</strong>选项卡,把<strong>Editor Attaching</strong>勾上,重启Unity和Mono Develop)</p>
</blockquote>
<p>##总结
Unity给开发者提供了一组Editor 类来制作自定义的编辑器窗口。在Asset Store上有很多插件,有些插件制作了非常漂亮的工具编辑器窗口。都是采用这些Editor来实现的。根据您的开发需要,您完全可以利用Editor类制作您自己的编辑器工具。</p>