tmanaの開発メモ

開発時の記録など書いていく予定です。

【CEDEC 2016】セッション:ユーザー行動ログとユーザーの感情を結びつける新しい分析手法

CEDEC2016で行われたセッションのまとめ記事です。

セッション詳細はこちら
実際のセッション資料はこちら※CEDiLアカウント(無料)が必要です。

今回セッションで紹介された内容は
「改修優先順位の決定事例」
「CM方向性の決定事例」
「行動ログとゲーム内アンケート融合の事例」
の3つでしたが、「CM方向性の決定事例」については省略します。

「改修優先順位の決定事例」

DeNAの逆転オセロニアでは、ユーザーが不満な点の把握と改修優先順位の決定にマーケティングリサーチを活用しているようです。
具体的には、ゲームの改修すべき場所をゲーム内アンケートで調査し、CSポートフォリオ分析で最終的な優先度を決定しているようです。
CSポートフォリオ分析とは、「項目満足度」と「総合満足度と項目別満足度の相関」を平面上の4象限にプロットすることで改修優先度を可視化する手法です。

ゲーム内アンケートで要素の満足度を調査
f:id:tmanadev:20160829021848j:plain:w300f:id:tmanadev:20160829085934j:plain:w300

CSポートフォリオ分析で優先度を決定
f:id:tmanadev:20160829021908j:plain:w300f:id:tmanadev:20160829090031j:plain:w300

なぜCSポートフォリオ分析を用いるのかと言うと、「満足度が低い=改修優先度が高い」とは限らないからです。
f:id:tmanadev:20160829021925j:plain:w400

アンケート要素に左右される部分がありますが、ユーザーがどこに満足し、逆にどこに不満があるのか実際にプレイしてもらっている人の意見を取れると改修がスムーズに行えそうですね。
また、改修に限らずイベント事にも使えそうです。
アンケートに答えてもらえるか、という問題も回答者にアイテム等を配布すると解決しそうですし、今後ゲームを開発する際はアンケートを実施するということも視野に入れた方が良いかもしれません。

「行動ログとゲーム内アンケート融合の事例」

上記のゲーム内アンケートに行動ログを合わせることで、ユーザーニーズに沿った改善を行うことも出来ます。
行動ログとは、ユーザーの行動(ダウンロード、ログイン、バトル、イベント参加など)を記録したログのことです。
f:id:tmanadev:20160829090254j:plain:w400

ログイン日数(行動ログ)と年代(アンケート)を組み合わせることでライト・ミドル・ヘビーユーザーの年代事の割合などを知る。
f:id:tmanadev:20160829023131j:plain:w400

他にも、ダウンロード日(行動ログ)と満足度(アンケート)の組み合わせでアップデート後の既存・新規ユーザーの状態把握や、
イベント事の参加率(行動ログ)と満足度(アンケート)からユーザー事のイベント難易度の調整等が行えるといった内容がありますが
f:id:tmanadev:20160829090542j:plain:w300f:id:tmanadev:20160829090610j:plain:w300
特に気になった内容が以下です。
f:id:tmanadev:20160829023158j:plain:w400
そう、ゲームをやめてしまった人の情報も分かることです。
一度やめてしまった人がもう一度やり始めることはほぼないと思いますが、同じ理由で辞めていく人は少なくなると思います。
f:id:tmanadev:20160829023231j:plain:w400

感想

今まで運営側の感覚で開発していたものがデータとして可視化することでより確実な改修が出来るのは勿論、今後新しいゲームを作る際にも参考になりますし、同じ過ちを繰り返すこともなくなるのではと思います。大変興味深いセッションでした。

【CEDEC 2016】初CEDEC を終えて

今回内定先の企業様から資金面をサポートしていただく、という形で初めてCEDECに参加してきました。
学生の身分な上に地方出身の私にとっては参加自体が難しかったため大変助かりました。
本当にありがとうございました!!

細かな内容部分はまた別の記事として上げるので、ここでは全体的な感想を書きます。

学び

私は学生時代に主にWeb系の開発をメインにやってきました。なので、今回CEDECに参加しようと思った理由も
・実際に公開されているゲームはどのようにして作られているのか
・ゲーム開発にはどのような技術があるのか
・どのような想定で開発を進めていくのか

といったことを知りたいと思ったからです。
結論としては大変満足の良くものでした。
また、それとは別にゲーム開発において技術に限界はなく、開発一つ一つに物凄い工夫と愛情がそそがれているということも実感しました。

Web系の開発は必要要件さえ満たしていればそこにこだわりを持つことはあまりないので(勿論人によりますが、要件から外れる場合が多いので)ゲーム開発での一人のキャラクターへのこだわりや愛情は惹かれるものがあり、改めてゲームエンジニアになりたいと強く思いました。
また、自分の技術力の足らなさも痛感しました。
CEDECでのセッションではコンシューマとモバイル両方参加してきましたが、技術を知らないために分からないことも多く、また、技術検証等の開発を一人で1ヵ月でやりとげる話を聞くと自分も出来るようになりたいと感じます。

目標

技術検証等の開発を一人で行えるようになること。
期間として1,2年でしょうか。企画や3Dモデリング、シェーダーやエフェクトやデザイン等を含めると分からないですが、プログラムだけで言えばそれだけで十分だと思います(勿論頑張ればですが)。幸いなことに学べる環境はありますので頑張ります。

【JavaScript】 文字と画像を一緒に入力する

お知らせの内容を作成する場合とかに画像と文字を一緒に入力したかったので、簡易の入力フォーム作成メモ。
Chromeで実行。IEでは動かない。
デモページ作成予定

今回作ったもの

文字と画像を一緒に入力できるフォーム。

準備するもの

今回AngulerJSを使用しましたが、AngularJSがなくてもいけると思います。なので特になし。

ソース

JavaScript

(function() {
    'use strict';

    var app = angular.module('myApp', []);
    app.directive('fileModel',function($parse){
        return{
            restrict: 'A',
            link: function(scope,element,attrs){
                var model = $parse(attrs.fileModel);
                element.bind('change',function(){
                    scope.$apply(function(){
                        model.assign(scope,element[0].files[0]);
                    });
                });
            }
        };
    });

    app.controller('FileController', ['$scope', function($scope) {
        var files = [];

        // imgタグにして返す
        var toImgHtml = function(src) {
            return '<img src="' + src + '" style="max-width: 100%; height: auto;">';
        }

        // imgタグを[!index]に変換する
        var convartImgIntoSymbol = function(htmlStr) {
            var str = htmlStr;
            str = str.replace(/\[/g,'[[').replace(/\]/g,']]');
            var symbolPrefix = '[!';
            var symbolSuffix = ']';
            for (var i in files) {
                while (str.indexOf(toImgHtml(files[i])) != -1) {
                    str = str.replace(toImgHtml(files[i]), symbolPrefix + i + symbolSuffix);
                }
            }
            return str;
        }

        // jqLite用element取得
        var getJqLiteElemById = function(id) {
            var elem = document.getElementById(id);
            return angular.element(elem);
        };

        // htmlタグを除いたテキストを取得
        var getText = function() {
            var str = convartImgIntoSymbol('' + getJqLiteElemById('userText').html());
            var strArray = str.split(/\r\n|\r|\n/);
            str = "";
            for (var i in strArray) {
                str += strArray[i].trim();
            }
            str = str.replace(/<div>/g, '\n');
            var div = document.createElement('div');
            return angular.element(div).html(str).text();
        };

        $scope.log = function() {
            console.log(getText());
        };

        //変化を監視して画像読み込み+表示を実行
        $scope.$watch("file",function(file){
            // 選択画像のリセット
            getJqLiteElemById('inputFile').val('');
            
            $scope.srcUrl = undefined;
            
            //画像ファイルじゃなければ何もしない
            if(!file || !file.type.match("image.*")){
                return;
            }

            var reader = new FileReader();

            //callback
            reader.onload = function(){
                $scope.$apply(function(){
                    $scope.srcUrl = reader.result;
                });
                files.push(reader.result);
                getJqLiteElemById('userText').append('<div>' + toImgHtml(reader.result) + '</div>');
            };

            //read as url(reader.result = url)
            reader.readAsDataURL(file)
        });
    }]);
}());

HTML

<div ng-app="myApp" ng-controller="FileController">
	<div class="input-text" ng-model="hoge" id="userText" contenteditable="true" style="width: 300px; height: 200px; background-color: #eef; border: 1px solid;margin-bottom: 10px;">
		
	</div>
	<label for="inputFile" style="background-color: #ccc;padding: 6px;border-radius: 12px;">
		画像を選択
		<input type="file" id="inputFile" file-model="file" style="display: none;">	
	</label>
	
	<label ng-click="log()" style="background-color: #ccc;padding: 6px;border-radius: 12px;margin-left: 10px;">
		Log
	</label>
</div>

解説

画像ファイルの取得に関してはこちらのサイト様のソースを利用しています。
http://qiita.com/zaburo/items/f03433caa710902d599f

コンテンツの編集はdivタグにcontenteditable属性を付けることで可能にしています(HTML5)。
取得したテキストは、(なぜか)改行にdivタグが使われているのでテキストを取得する際はdivタグを改行文字に変換しています。

str = str.replace(/<div>/g, '\n');

また、取得したテキストに謎の空白が入ったりbrタグが入ったりするので以下の処理を加えています。

謎の空白の削除

var strArray = str.split(/\r\n|\r|\n/);
str = "";
for (var i in strArray) {
	str += strArray[i].trim();
}

htmlタグの削除

angular.element(div).html(str).text();

今回はhtmlタグを除きたかったので上記を使用しましたが、こちらにエスケープとしての機能はないので気を付けて下さい。
エスケープについてはこちらのサイト様が詳しく書いてました。
http://iwb.jp/jquery-javascript-html-escape/

imgタグの変換はサーバの方で文章と画像を別々に取り扱いたかったので書きました。
文章を表示する際に記号と画像を戻してください。

参考サイト

https://blogs.msdn.microsoft.com/osamum/2014/02/13/html5api-contenteditable/

http://curtaincall.weblike.jp/blog/?p=190

http://qiita.com/zaburo/items/f03433caa710902d599f

http://www.todesking.com/blog/2015-02-05-html-escape-with-innertext-and-innerhtml-is-not-enough/