モデラート - C#とゲーム開発と雑記 このページをアンテナに追加 RSSフィード

2012-03-27

[][][] text_edit_object_with_tkinter.py を Blender 2.62 で動かす 01:06  text_edit_object_with_tkinter.py を Blender 2.62 で動かすを含むブックマーク  text_edit_object_with_tkinter.py を Blender 2.62 で動かすのブックマークコメント

リソース・チュートリアル : 最近の Blender 2.5の Text オブジェクトの日本語対応 Blender.jp

この記事で紹介されているスクリプトを修正し 2.62 で動作するところまでこぎつけました。

動作確認をしたのは Windows XP 32bit、Windows 7 64bit のみです。

Python は 3.2 です。


text_edit_object_with_tkinter_2_62.py

※この記事の最後にスクリプトの全文を記載しています。


インストール

Python のパスがうまく解決できなかったため、とりあえずファイルをコピーして動作するのを確認しています。このあたりは出来れば解決したい……

次のファイルを Blenderpython ディレクトリにコピーしてください。

(\Blender Foundation\Blender\2.62\python 以下)


  • Python32\DLLs
  • Python32\tcl
  • Python32\Lib\tkinter

スクリプトは普通にアドオンのインストールの手順でインストールしてください。

アドオンのカテゴリーは Text です。

名称は Edit text object with Tkinter となっていて元のスクリプトから変更しておりません。


使用方法

ウィンドウを表示するには、Text Object を追加し、選択し、Properties -> Object Data を選択し、一番下の Edit Text Object ボタンをクリックしてください。

Load Fonts ボタンの方は何も変更を加えていないので動かないかもしれません。

その場合フォントは適切なフォントを選択しておいてください。

デフォルトフォントでは日本語が入っていない(?)ため、日本語の文字が表示されません。


ウィンドウを表示したときにランタイムエラーが発生する場合、次のファイルをリネーム等して読み込まれない状態にすればエラーは表示されなくなると思います。

\Blender Foundation\Blender\msvcr90.dll


ウィンドウが表示されている状態でも Blender 上で操作は可能です。

ただし、ウィンドウを表示するときに選択されていたオブジェクトを参照しているので、別の Text Object を選択しテキストを編集しても最初に選択していた Text Object に変更が反映されます。

また、最初に選択していた Text Object を削除した場合正しく動作しません。


ウィンドウ内でテキストを書き換えるとリアルタイムに 3D View 上の Text Object に反映します。

Apply ボタンを押下するか、×ボタンでウィンドウを閉じて編集を完了してください。

Cancel ボタンを押下すると、ウィンドウを立ち上げたときの内容のテキストに差し戻します。


text_edit_object_with_tkinter_2_62.py

# coding: utf-8

# ##### BEGIN GPL LICENSE BLOCK #####
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License
#  as published by the Free Software Foundation; either version 2
#  of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software Foundation,
#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# ##### END GPL LICENSE BLOCK #####


bl_info = {
    'name': 'Edit text object with Tkinter',
    'author': 'chromoly',
    'version': (0, 0, 1),
    'blender': (2, 6, 2),
    'api': 32385,
    'location': 'Properties -> ObjectData',
    'description': '',
    'warning': '',
    'category': 'Text'}

'''
日本語入力の為、TkinterのGUIを呼び出します。GUIを呼び出している間、Blenderは止まります。
参考: http://docs.python.org/py3k/library/tkinter.html
      Pythonプログラミング入門

'''

import os
import sys
import threading

#### 環境に合わせて修正してください #############################################
## Tkinter path
'''
importエラーを吐くようなら、モジュールパスを修正してください。
'''
try:
    import tkinter as tk
except:
    if sys.platform == 'win32':  # Windows
        sys.path.append('C:\\Python32\\Lib')
        sys.path.append('C:\\Python32\\DLLs')
        os.environ['TCL_LIBRARY'] = 'C:\\Python32\\tcl\\tcl8.5'
    else:  # sys.platform == 'linux2'  etc...
        prefix = '/usr'
        dirname = 'python' + sys.version[:3]  # python3.1
        sys.path.append(os.path.join(prefix, 'lib', dirname))
        sys.path.append(os.path.join(prefix, 'lib', dirname, 'lib-dynload'))

## Load Fonts
'''
'Load Fonts'ボタンで複数のフォントを一度に読み込みます。
'''
if sys.platform == 'win32':  # Windows
    font_paths = ('C:\WINDOWS\Fonts\msgoth04.ttc',
                  'C:\WINDOWS\Fonts\msmin04.ttc')
else:  # Linux etc.
    font_paths = ('/home/hoge/.fonts/meiryo/meiryo.ttc',
                  '/home/hoge/.fonts/meiryo/meiryob.ttc')

## Tkinter GUI
'''
FONTNAMEは、利用可能なフォント名を指定してください。
読み込めない場合はデフォルトのフォントが使われます。
'''
FONTNAME = 'Meiryo'
FONTSIZE = 11
###############################################################################

import tkinter as tk
from tkinter.scrolledtext import ScrolledText
from tkinter.filedialog import askopenfilename, asksaveasfilename
import bpy
from bpy.props import *
import time

class Scroller(tk.Frame):
    def __init__(self, master=None, textobj=None):
        tk.Frame.__init__(self, master)
        self.pack()
        self.cached_text = ""

        # <Title>
        master.title('Edit TextObject')

        # <Menu>
        self.mb = tk.Menu(master, font=(FONTNAME, FONTSIZE))

        self.fm = tk.Menu(self.mb, font=(FONTNAME, FONTSIZE), tearoff=0)
        self.fm.add_command(label='開く', command=self.openFile)
        self.fm.add_command(label='別名で保存', command=self.saveFile)
        self.fm.add_separator()
        self.fm.add_command(label='終了', command=self.quit)  # master.quit
        self.mb.add_cascade(label='ファイル', menu=self.fm)

        self.em = tk.Menu(self.mb, font=(FONTNAME, FONTSIZE), tearoff=0)
        self.em.add_command(label='切り取り', command=self.cutSelection)
        self.em.add_command(label='複製', command=self.copySelection)
        self.em.add_command(label='貼り付け', command=self.pasteClip)
        self.mb.add_cascade(label='編集', menu=self.em)

        master.config(menu=self.mb)

        # <Shortcut>  master.bindだと二回繰り返す事に
        self.bind('<Control-KeyPress-x>', self.cutSelection)
        self.bind('<Control-KeyPress-c>', self.copySelection)
        self.bind('<Control-KeyPress-v>', self.pasteClip)

        # <Button>
        f1 = tk.Frame(master, relief=tk.SUNKEN, bd=1)
        f1.pack()
        self.bpy_clear = tk.Button(f1, text='Clear', \
                                   font=(FONTNAME, FONTSIZE), \
                                   command=self.bpyClear, width=12)
        self.bpy_clear.pack(side='left')
        '''
        self.bpy_read = tk.Button(f1, text='Read', \
                                  command=self.bpyRead, width=12)
        self.bpy_read.pack(side='left')
        self.bpy_apply = tk.Button(f1, text='Apply', width=12)
        self.bpy_apply['command'] = self.bpyApply  # こんな書き方も
        self.bpy_apply.pack(side='left')
        '''
        self.bpy_applyquit = tk.Button(f1, text='Apply', \
                                       font=(FONTNAME, FONTSIZE),\
                                       command=self.bpyApplyQuit, width=12)
        self.bpy_applyquit.pack(side='left')

        self.bpy_quit = tk.Button(f1, text='Cancel', \
                                  font=(FONTNAME, FONTSIZE), \
                                  command=self.bpyCancel, width=12)
        self.bpy_quit.pack(side='right')

        # <Text>
        self.st = ScrolledText(master, font=(FONTNAME, FONTSIZE))
        self.st.pack(fill=tk.BOTH, expand=1)
        self.st.focus_set()
        
        # <Read from bpy.context.active_object>
        self.setTextObject(textobj)

    def setTextObject(self, txtobj):
        self.text_active_object = txtobj
        
    def getTextObject(self):
        ob = self.text_active_object
        if ob and ob.type == 'FONT':
            return ob
        return None

    def bpyClear(self):
        self.st.delete('1.0', tk.END)

    def bpyRead(self):
        ob = self.getTextObject()
        if ob:
            editmode = ob.mode
            if editmode == 'EDIT':
                bpy.ops.object.mode_set(mode='OBJECT')
            text = ob.data.body
            self.cached_text = text
            self.st.delete('1.0', tk.END)
            self.st.insert(tk.END, text)
            if editmode == 'EDIT':
                bpy.ops.object.mode_set(mode='EDIT')

    def setText(self, str):
        ob = self.getTextObject()
        if ob:
            editmode = ob.mode
            if editmode == 'EDIT':
                bpy.ops.object.mode_set(mode='OBJECT')
            ob.data.body = str
            if editmode == 'EDIT':
                bpy.ops.object.mode_set(mode='EDIT')        
                
    def bpyApply(self):
        self.setText(self.st.get('1.0', tk.END))

    def bpyApplyQuit(self):
        self.bpyApply()
        self.quit()
        
    def bpyCancel(self):
        self.setText(self.cached_text)
        self.quit()

    def openFile(self):
        fn = askopenfilename()
        if fn:
            fi = open(fn, 'r')
            b = fi.read()
            fi.close()
            self.st.delete('1.0', tk.END)
            self.st.insert(tk.END, b)

    def saveFile(self):
        fn = asksaveasfilename()
        if fn:
            fo = open(fn, 'w')
            fo.write(self.st.get('1.0', tk.END))
            fo.close()

    def cutSelection(self, *tmp):
        if self.st.tag_ranges(tk.SEL):
            self.copySelection()
            self.st.delete(tk.SEL_FIRST, tk.SEL_LAST)

    def copySelection(self, *tmp):
        if self.st.tag_ranges(tk.SEL):
            t = self.st.get(tk.SEL_FIRST, tk.SEL_LAST)
            self.st.clipboard_clear()
            self.st.clipboard_append(t)

    def pasteClip(self, *tmp):
        if self.st.tag_ranges(tk.SEL):
            self.st.delete(tk.SEL_FIRST, tk.SEL_LAST)
        t = self.st.selection_get(selection='CLIPBOARD')
        self.st.insert(tk.INSERT, t)

class Invoker(threading.Thread):
    def __init__(self, m):
        threading.Thread.__init__(self)
        self.method = m

    def run(self):
        self.method()

class TEXT_OT_edit_object_with_tkinter(bpy.types.Operator):
    '''Call Tkinter GUI'''
    bl_description = 'Call Tkinter GUI and Blender stop'
    bl_idname = 'text.edit_object_with_tkinter'
    bl_label = 'Call Tkinter GUI'

    @classmethod
    def poll(cls, context):
        actob = context.active_object
        return actob and actob.type == 'FONT'

    def pre_call(self):
        self.root = tk.Tk()
        self.app = Scroller(master=self.root, textobj=bpy.context.active_object)
        
        self.app.st.bind("<ButtonRelease>", self.callback)
        self.app.st.bind("<KeyRelease>", self.callback)
        
    def callback(self, event):
        self.app.bpyApply()
        
    def call_tk(self):
        self.app.bpyRead()
        self.app.mainloop()
        try:
            self.root.destroy()
        except:
            pass
        del self.app
        del self.root
        del self.t
        self._timer = bpy.context.window_manager.event_timer_add(0.1, bpy.context.window)
            
    def execute(self, context):
        context.window_manager.modal_handler_add(self)        
        self.pre_call()        
        self.t = Invoker(self.call_tk)
        self.t.start()        
        return {'RUNNING_MODAL'}
        
    def cancel(self, context):
        return {'CANCELLED'}

    def modal(self, context, event):
        if event.type == 'ESC':  # Cancel
            return self.cancel(context)
        if event.type == 'TIMER':
            context.window_manager.event_timer_remove(self._timer)
            del self._timer
            return {'CANCELLED'}
        
        return {'PASS_THROUGH'}


class TEXT_OT_load_fonts(bpy.types.Operator):
    '''Load fonts'''
    bl_description = 'Load fonts. ' + \
                   '(edit "text_edit_object_with_tkinter.py":62~76:font_paths)'
    bl_idname = 'text.load_fonts'
    bl_label = 'Load Fonts'

    setactob = BoolProperty(default=False)

    '''
    @classmethod
    def poll(cls, context):
        actob = context.active_object
        return actob and actob.type == 'FONT'
    '''

    def execute(self, context=None):
        if not font_paths:
            return {'FINISHED'}
        loadedpaths = [f.filepath for f in bpy.data.fonts]
        for path in font_paths:
            if not path in loadedpaths:
                bpy.ops.font.open(filepath=path)

        if self.setactob:
            actob = bpy.context.active_object
            if actob and actob.type == 'FONT':
                for font in bpy.data.fonts:
                    if font.filepath == font_paths[0]:
                        actob.data.font = font
                        break
        return {'FINISHED'}


class DATA_PT_call_tkinter(bpy.types.Panel):
    bl_label = "Edit Text with Tkinter"
    COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
    bl_space_type = 'PROPERTIES'
    bl_region_type = 'WINDOW'
    bl_context = 'data'

    @classmethod
    def poll(cls, context):
        '''engine = context.scene.render.engine
        actob = context.active_object
        return (actob and actob.type == 'FONT' and \
                (engine in cls.COMPAT_ENGINES))
        '''
        actob = context.active_object
        return (actob and actob.type == 'FONT')

    def draw(self, context):
        layout = self.layout
        row = layout.row()
        row = row.split(percentage=0.7)
        row.operator_context = 'INVOKE_DEFAULT'
        op = row.operator('text.edit_object_with_tkinter', text='Edit Text Object')
        op = row.operator('text.load_fonts', text='Load Fonts')
        op.setactob = True


# Register
def register():
    bpy.utils.register_class(DATA_PT_call_tkinter)
    bpy.utils.register_class(TEXT_OT_edit_object_with_tkinter)
    bpy.utils.register_class(TEXT_OT_load_fonts)


def unregister():
    bpy.utils.unregister_class(DATA_PT_call_tkinter)
    bpy.utils.unregister_class(TEXT_OT_edit_object_with_tkinter)
    bpy.utils.unregister_class(TEXT_OT_load_fonts)


if __name__ == '__main__':
    register()
トラックバック - http://d.hatena.ne.jp/runicalp/20120327

2012-03-23

[]いかにしておっぱい画像をダウンロードするか〜2012 をC#で書きました 01:15 いかにしておっぱい画像をダウンロードするか〜2012 をC#で書きましたを含むブックマーク いかにしておっぱい画像をダウンロードするか〜2012 をC#で書きましたのブックマークコメント

元ネタ

いかにしておっぱい画像をダウンロードするか〜2012


他にもいくつかの言語で書かれています。

はてなダイアリー


Bing API の制限?があるらしく、ダウンロード枚数が1000枚に届く前に終了してしまいます。

依存ライブラリ

DynamicJson 1.2.0.0

Interactive Extensions 1.1.10621

Program.cs

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using System.Web;
using System.Web.Security;
using Codeplex.Data;

namespace App
{
    class Program
    {
        static void Main()
        {
            const string appid = "";
            const string dir = "./data";
            Directory.CreateDirectory(dir);
            const int num = 50;

            foreach (var response in Enumerable.Range(0, int.MaxValue)
                .Select(_ => (long)_ * 50)
                .Select(page =>
                {
                    var q = HttpUtility.ParseQueryString(string.Empty);
                    q.Add(new NameValueCollection
                        {
                            {"AppId", appid},
                            {"Version", "2.2"},
                            {"Markert", "ja-JP"},
                            {"Sources", "Image"},
                            {"Image.Count", num.ToString(CultureInfo.InvariantCulture)},
                            {"Image.Offset", page.ToString(CultureInfo.InvariantCulture)},
                            {"Adult", "off"},
                            {"Query", "おっぱい"}
                        });
                    return q;
                })
                .Select(q => new Uri("http://api.bing.net/json.aspx?" + HttpUtility.UrlDecode(q.ToString())))
                .Select(WebRequest.CreateDefault)
                .Select(_ => _.GetResponse()))
            {
                string[] urls = EnumerableEx.Using(
                    response.GetResponseStream,
                    stream => (IEnumerable<string>)ToUrls(DynamicJson.Parse(stream)))
                    .ToArray();
                if (!urls.Any())
                {
                    Console.WriteLine("error");
                    return;
                }

                Parallel.ForEach(
                    urls.Select(_ => new
                    {
                        url = _,
                        ext = Path.GetExtension(_),
                        fileName = Path.Combine(dir,
                                                FormsAuthentication.HashPasswordForStoringInConfigFile(
                                                    HttpUtility.UrlEncode(_), "md5") +
                                                Path.GetExtension(_))
                    })
                    .Where(_ => new[] {".jpg", ".jpeg", ".png", ".gif", ".bmp"}.Contains(_.ext)),
                    s =>
                    {
                        Console.WriteLine(s.url);
                        try
                        {
                            using(var wc = new WebClient())
                                wc.DownloadFile(s.url, s.fileName);
                        }
                        catch { }
                    });
            }
        }
        static IEnumerable<string> ToUrls(dynamic source)
        {
            if (source.IsDefined("SearchResponse") &&
                source.SearchResponse.IsDefined("Image") &&
                source.SearchResponse.Image.IsDefined("Results"))
            {
                foreach(var _ in source.SearchResponse.Image.Results)
                {
                    if (!_.IsDefined("MediaUrl")) continue;
                    yield return _.MediaUrl;
                }
            }
        }
    }
}

2012-02-05

[]変化するローカル変数を参照したラムダ式を作成しない 14:26 変化するローカル変数を参照したラムダ式を作成しないを含むブックマーク 変化するローカル変数を参照したラムダ式を作成しないのブックマークコメント

次のメソッドで生成されるラムダ式は、ローカル変数"_"を参照しています。

        static string[] data = { "a", "b", "c", "d" };
        private static IEnumerable<Action> InvokeAction()
        {
            foreach (var _ in data)
            {
                yield return () => Console.WriteLine(_);
            }
        }

これは次の2つのコードで異なる結果を生成します。

※要 Interactive Extensions

        static void Main()
        {
            InvokeAction().ForEach(_ => _());

            InvokeAction().ToList().ForEach(_ => _());
        }
a
b
c
d

d
d
d
d

上のコードの場合、ローカル変数は"a","b","c","d"に初期化され、その都度Console.WriteLineが実行されますが、下のコードの場合はローカル変数が最後に初期化された値でConsole.WriteLineが実行されています。

このような変化するローカル変数を参照したラムダ式を作るとResharperが"Access to modified closure"と知らせてくれます。


どちらの場合でも同じ結果にするためには、ローカル変数を参照せずその値を評価したラムダ式を返すようにすれば良いです。


今回は、Expression TreeとILでDelegateを作成してみます。


次のメソッドで、() => Console.WriteLine(_); を CreateDelegate(_); という呼び出しに変更します。

        private static Action CreateDelegate(string type)
        {
            return Expression.Lambda<Action>(
                Expression.Call(
                    new Action<string>(Console.WriteLine).Method,
                    Expression.Constant(type))).Compile();
        }

        private static Action CreateDelegateIL(string type)
        {
            var dm = new DynamicMethod(string.Empty, typeof (void), new[] {typeof (string)});
            var ilgen = dm.GetILGenerator();
            ilgen.Emit(OpCodes.Ldstr, type);
            ilgen.Emit(OpCodes.Call, new Action<string>(Console.WriteLine).Method);
            ilgen.Emit(OpCodes.Ret);
            return (Action)dm.CreateDelegate(typeof (Action), null);
        }

Expression Treeを使うか、ILを使うかの違いでそれぞれ生成されるDelegateは同じです。

string _ = "a";
CreateDelegate(_);

これで

() => Console.WriteLine("a");

この様なコードを生成します。


これでローカル変数を参照ではなく、ローカル変数の値を評価したうえでのラムダ式を生成することが出来ました。


using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection.Emit;

namespace ConsoleApplication1
{
    class Program
    {
        static string[] data = { "a", "b", "c", "d" };
        static void Main()
        {
            InvokeAction().ForEach(_ => _());
            Console.WriteLine();

            InvokeAction().ToList().ForEach(_ => _());
            Console.WriteLine();

            InvokeDelegate().ToList().ForEach(_ => _());
            Console.WriteLine();

            InvokeDelegateIL().ToList().ForEach(_ => _());
        }

        private static IEnumerable<Action> InvokeAction()
        {
            foreach (var _ in data)
            {
                yield return () => Console.WriteLine(_);
            }
        }

        private static IEnumerable<Action> InvokeDelegate()
        {
            foreach (var _ in data)
            {
                yield return CreateDelegate(_);
            }
        }

        private static IEnumerable<Action> InvokeDelegateIL()
        {
            foreach (var _ in data)
            {
                yield return CreateDelegateIL(_);
            }
        }

        private static Action CreateDelegate(string type)
        {
            return Expression.Lambda<Action>(
                Expression.Call(
                    new Action<string>(Console.WriteLine).Method,
                    Expression.Constant(type))).Compile();
        }

        private static Action CreateDelegateIL(string type)
        {
            var dm = new DynamicMethod(string.Empty, typeof (void), new[] {typeof (string)});
            var ilgen = dm.GetILGenerator();
            ilgen.Emit(OpCodes.Ldstr, type);
            ilgen.Emit(OpCodes.Call, new Action<string>(Console.WriteLine).Method);
            ilgen.Emit(OpCodes.Ret);
            return (Action)dm.CreateDelegate(typeof (Action), null);
        }
    }
}
トラックバック - http://d.hatena.ne.jp/runicalp/20120205

2012-02-01

[][][]GlobalGameJam 2012 札幌会場に参加してきました。 01:37 GlobalGameJam 2012 札幌会場に参加してきました。を含むブックマーク GlobalGameJam 2012 札幌会場に参加してきました。のブックマークコメント


1/27、28、29と3日に渡り開催された48時間でゲームを作るイベントに参加してきました。

僕のいたチームが作った作品はこのページからダウンロードして遊ぶことが出来ます。

http://globalgamejam.org/2012/%E3%81%8E%E3%81%93%E3%81%A1%E3%81%90%E3%82%8F%E3%81%A1%E3%81%8A


27日は夕方からの開始だったのですが会社にGGJへ参加する旨を伝えると休みを取れと言われ休みになりました。

Unityでの開発経験はなく、少しでも足しになればと当日の午前中はUnityの勉強をしていました。

Blenderからアニメーション付きのFBXファイルを吐き出し、モーションの切り替えを試していたのですがうまく行かずに終わってしまいました。今になって思うのはデータのやり取りなどそのあたりを勉強するべきでしたね。


このチームはUnityでの開発だったのですが、2人のプログラマーは触り始めて1週間程度でした。

ですが、もう一人のプログラマーの活躍で形にすることが出来ました。

ほとんどUnityに触れた時間は変わらないはずなのにとても頼りになりました!ありがとうそしてありがとう


僕がプログラムを担当したのは曲の再生だったり、流れてくる譜面だったりそのあたりをやっていました。

Unityシーケンサーというキーワードで思い出したのがこの記事

http://radiumsoftware.tumblr.com/post/13189647626

この記事を見たときはUnityは使っていないのですが、たまたま思い出しとても参考になりました。


今回このゲームで使用した曲は8小節ごとに別ファイルにしてもらっており、それを指定した順に指定した回数で再生することが出来るようになっています。

時間も差し迫っていたので、最初に用意された順番のまま使用され、その機能が活用されることはありませんでした。


譜面のデータはC#エディタを作りそれを使用しました。このゲームは何気に2つの難易度が選べるようになっているのですが、このエディタを作っていなければもう一つの難易度の実装は出来なかったと思います。たぶん。そうだと信じたい


それとタイトルなども担当しました。

あのタイトルは仮なのですが、そのアイディアは最終日の昼ご飯の帰りの雑談で決めたものでした。

新たに素材を起こすことなく、かつ蛇の気持ち悪さを前面に出したなんかそんな感じの。

そして2Dの扱いが最後までよくわからず適当です。解像度によってはボタンが画面外に…


知人や会社の人にやってもらった感想も、企画段階に意図したもので大変満足しています。

やりたい要素はもっともっとあったのですが、最低限の部分を入れ込み仕上げられたことにも満足しています。


ゲームと関係のない仕事を1年ほど続けていたのですが、久しぶりにゲーム作りができてとても楽しかったです。

またこういうのやりたいですね。

そして、Unityは大変すばらしいツールです!

C# もすばらしいですね!

BlenderBlender


最後に、今回僕のチームに女の子を入れてくれなかったという仕打ちは忘れません(泣)

トラックバック - http://d.hatena.ne.jp/runicalp/20120201

2011-08-20

[]ゲームプログラミング勉強会 札幌 1.0に行ってきました。 02:14 ゲームプログラミング勉強会 札幌 1.0に行ってきました。を含むブックマーク ゲームプログラミング勉強会 札幌 1.0に行ってきました。のブックマークコメント

最近仕事ではゲームの開発から離れてしまっているので刺激を受けるために参加してきました。

Twitterでよく知っている人を何人か拝見できましたし、充実したものになりました。

今話題のUnityソーシャルゲームの講演もありましたが、個人的には3Dの深い話しが聞きたかったですね。

内容を一部書き忘れていると思います。その点ご了承下さい。


Unity入門(@mswar777さん)

ゲーム制作の大まかなフローやAsset Store、スクリプトがゲーム動かしながらいじれるよ!という話しなどがありました。

弊社でも使用しているようですが、僕は関わっていないので個人的にインストールしてどんなものか試してみようしていました。ですが、何から手を付けれよいのかわからず放置していた、そんな状況でした。


東方同人ゲーム(幻想テレグノシス)の紹介もされていました。

公式にあるチュートリアルの存在を知れたのは収穫でした。これを利用して学習していくのがいいかもしれません。


これからのゲーム制作の話しは興味深かったです。

Minecraftのようにオンライン上で複数の人が同時に作業しゲームを制作するという話し。おもしろそう。


LuaSquirrelによるゲーム開発入門(@lapis_twさん)

Luaの採用事例やバインドの話しなどがありました。

コンシューマーゲーム開発(に限りませんが)におけるスクリプトは最早なくてはならないものですね。

それでも去年途中で投入されたプロジェクトではスクリプトを使用しておらずかなり驚きました。

僕は自社で開発したスクリプトを使用していてLuaSquirrelも使用しているわけではないので、今後別のプロジェクトではどちらか採用しようと思っています。Squirrelの方がよさげ?


cocos2dによるiPhoneゲーム開発入門

cocos2dというゲームフレームワークの話しでした。

個人的にMacは持っていないので手を上げたのですが、仕事でiPhoneアプリの開発をやっていました。

Objective-Cにある程度慣れてしまえば、このライブラリを使って簡単にゲームが作れてしまうのではないかと錯覚してしまう講演でした。


以下、LT

ソーシャルゲーム(@U_TASさん)(変更後のタイトル覚えてないです)

ソーシャルゲームとは何ぞやという話しなど。

いつかのゲームAIラウンドテーブルの時にソーシャルゲームが題材になったことがあったのですが、そのときに聞いたものとは違いました。

ソーシャルゲームというものはコンシューマゲームと区別するための名前に過ぎず定義があいまいというように言われていましたがそれは違うのではないかと…

区別する必要があるとは思えませんし、お互いいいとこ取りして切磋琢磨すれば良いと思います。


ソーシャルブラウザゲームについて(@nazoさん)

この講演はスライドが印象的でした。

ソーシャルゲームが悪く言われる面や、サーバーサイドの技術の話しなど。

僕はソーシャルゲームの儲け方があまり好きではないのですが、サーバー側で最先端技術使いまくりという話しを聞いて印象がかわりましたね。サーバー技術者としてはソーシャルゲームは面白いものなんだろうなぁと思います。


GlobalGameJam2012開催について(@geekdrumsさん)

LT童貞とは思えない見事な喋りでした。

来年のGGJと福島GGJにあわせて開催されるKawazGameJamのお話しでした。

来年のGGJは是非参加したいな…


Squirrelによる自作クロスプラットフォームゲームエンジンについて(@infosiaさん)

AndroidiOSで動くゲームエンジンemo-frameworkのお話しでした。

かわずたんたたきをこのエンジンで動くように移植したそうです。

Androidでゲームを作る機会があったらちょっと見てみましょう。


ちなみに会場にはお菓子が用意されてました。


全ての講演が終わった後にかわずたんがデザインされたマグカップの抽選会があり、なんと当たってしまいました。

f:id:runicalp:20110821015649j:image

デスクトップの伊波ちゃんかわいい


そんな感じで勉強会は終わりました。

参加された皆さんお疲れ様でした。


懇親会出ればよかったです…

トラックバック - http://d.hatena.ne.jp/runicalp/20110820
Connection: close