やったこと
最近できるようになったこと:自分で仕事を受けられるようになった
自分で見積書と請求書が出せるようになりました!
(インボイスはやっていません。ごめんね)
出来る人からすれば「そんなんラクショーだろ、何が難しいんだ」、という感じだと思うのですが、今までは、ほぼほぼ、システム化されているところでぽいっと貰うだけだったので、能動的に代金を請求するのも、貰うのも、初めての経験でした。
はじめて請求書に書いた金額と同じ金額が振り込まれているのを見たときは、ちょっとしたパーティーでした。
今年の、特に後半は、「働きたくないな……」とボヤきながら、息抜きに副業をやっていたと思います。
自主的に請け負ったお仕事は辛くないんですよ。
まあ、本業があるので、生活資金を回収しなくてもよくてですね、焦ったり、ホンキで価格交渉をしなくても良いという面があるんですが、純粋に楽しかったです。
家から一歩も出る必要はなく、得意なことしかやらなくていい。
全てを自分でやるのは過酷すぎる、と思いつつ、もしかしたらこっちで生きていった方が幸せなんじゃないかと思う瞬間があるので、引き続き、ぼちぼちやっていきたいなと思っています。
[ogp]https://crowdworks.jp/public/employees/6635834[/ogp]
作ったゲーム
今年作ったゲームで一つピックアップするならMoonShotHunterだと思います。
[ogp]https://subara3.com/freegame/MoonShotHunting/index.html[/ogp]
独自性があり、一定のゲーム性があります。
イラストも
あきらさんに頼んだのでかわいいです。
作ったアプリ
iOSアプリを作りました。
[ogp]https://subara3.com/mobile_application/DoTanka/index.php[/ogp]
この私でもiOSアプリを作ってリリースできたという意味でたいへん意義のあるものだと思うのですが、強くお勧めというわけではないです。
それでも使ってもらえて嬉しーです。
あと、まともに作ったキャラクターというのが
たんかどりくんになってしまったので、
この子は可愛いですね。
たんかどり
ありがとー。
たんかどりくんの好物はギリシャヨーグルトです。
ほかにも、Webのツールをたくさん作りました。
一番時間をかけたのは、「ポン出しサムネメーカー」です。
[ogp]https://subara3.com/tool/pondashi_thumbnail_maker/index.html[/ogp]
今のところ、このくらいの複雑さのWebアプリケーションが作れるぞ、というベンチマークになっています。
いっぽうで、複雑なアプリケーションであれば便利なわけでもないというね。
書いた文章
どちらかというと創作よりも実用の文章・メモを書いた年で、それほどはアウトプットしませんでした。
クラウドソーシングで案件に応募するとき、実績を添えておくと便利なんですが、
会社のあれとかそれとかが(あんまり)ないので、
ホームページやQiitaで書いた技術記事が実績としてときどき役に立ちました。
このへん、アドベントカレンダーで表彰される以上にはいままであんまり恩恵を感じることはなかったんですが、いいこともあるなと思いました。
これからやりたいこと
Steamに行きたい
来年こそはSteamにゲームを出すのにチャレンジしたいです。
魅力的なキャラクターのゲームか、
あるいは、誰かの二次創作の器になるようなゲーム(Elinとか、CardWirthとか、自キャラを持ち込んで遊ぶタイプのゲーム)を作りたいです!
ファン活動(二次創作)
なんと、キングオブデモンズRの制作者さんの非公式オンリーイベント(Web)があるので、4コマ漫画本をつくりたいです。
[ogp]https://picrea.jp/event/63d5537f693522a872828c0e1856b57581ce8da83c12e51fdf91acea864221fa[/ogp]
今は線がへにょって気に入らないので曲線ツールを練習中です。
(function() {
const icon = document.querySelector('.tankadori-icon');
const wrapper = document.querySelector('.tankadori-icon-wrapper');
if (!icon || !wrapper) return;
let isFollowing = false;
let originalParent = icon.parentElement;
let originalNextSibling = icon.nextSibling;
let targetX = 0, targetY = 0;
let currentX = 0, currentY = 0;
let originalRect = null;
let floatY = 0;
// ふわふわ + 向き変更(ページ全体で反応)
function idleAnimate() {
if (isFollowing) return;
floatY = Math.sin(Date.now() / 500) * 4;
const rect = icon.getBoundingClientRect();
const iconCenterX = rect.left + rect.width / 2;
const facingRight = lastMouseX > iconCenterX;
if (facingRight) {
icon.style.transform = 'translateY(' + floatY + 'px) scaleX(-1)';
} else {
icon.style.transform = 'translateY(' + floatY + 'px) scaleX(1)';
}
requestAnimationFrame(idleAnimate);
}
let lastMouseX = 0;
document.addEventListener('mousemove', function(e) {
lastMouseX = e.clientX;
});
idleAnimate();
icon.addEventListener('click', function(e) {
e.stopPropagation();
if (!isFollowing) {
isFollowing = true;
originalRect = icon.getBoundingClientRect();
currentX = originalRect.left;
currentY = originalRect.top;
targetX = currentX;
targetY = currentY;
icon.style.position = 'fixed';
icon.style.left = currentX + 'px';
icon.style.top = currentY + 'px';
icon.style.zIndex = '99999';
icon.style.borderRadius = '0';
icon.style.border = 'none';
icon.style.width = '80px';
icon.style.height = '80px';
document.body.appendChild(icon);
document.addEventListener('mousemove', updateTarget);
flyAnimate();
} else {
isFollowing = false;
document.removeEventListener('mousemove', updateTarget);
const startX = currentX, startY = currentY;
const endX = originalRect.left, endY = originalRect.top;
const duration = 800;
const startTime = performance.now();
function returnAnimate(now) {
const elapsed = now - startTime;
const progress = Math.min(elapsed / duration, 1);
const ease = 1 - Math.pow(1 - progress, 3);
currentX = startX + (endX - startX) * ease;
currentY = startY + (endY - startY) * ease;
icon.style.left = currentX + 'px';
icon.style.top = currentY + 'px';
if (progress < 1) {
requestAnimationFrame(returnAnimate);
} else {
icon.style.position = '';
icon.style.zIndex = '';
icon.style.left = '';
icon.style.top = '';
icon.style.transform = '';
icon.style.borderRadius = '';
icon.style.border = '';
icon.style.width = '';
icon.style.height = '';
if (originalNextSibling) {
originalParent.insertBefore(icon, originalNextSibling);
} else {
originalParent.appendChild(icon);
}
idleAnimate();
}
}
requestAnimationFrame(returnAnimate);
}
});
function updateTarget(e) {
targetX = e.clientX - 40;
targetY = e.clientY - 40;
}
function flyAnimate() {
if (!isFollowing) return;
currentX += (targetX - currentX) * 0.08;
currentY += (targetY - currentY) * 0.08;
icon.style.left = currentX + 'px';
icon.style.top = currentY + 'px';
const iconCenterX = currentX + 40;
const cursorX = targetX + 40;
const facingLeft = cursorX < iconCenterX;
const wobble = Math.sin(Date.now() / 200) * 5;
if (facingLeft) {
icon.style.transform = 'rotate(' + wobble + 'deg)';
} else {
icon.style.transform = 'scaleX(-1) rotate(' + wobble + 'deg)';
}
requestAnimationFrame(flyAnimate);
}
})();