- 始まり
- アウトライン(輪郭線)とは
- さあ、教えてください。誰に興味があるんですの?
- 注意点なのよ
- プラグインとは
- プラグインを作る
- FSceneViewExtensionBase
- 超シンプルに作ってみた
- できた!!!
- ひと区切り
- 実装
- Source/OutlineRenderPipeline/Public/OutlineSettings.h
- Source/OutlineRenderPipeline/Private/OutlineSettings.cpp
- Source/OutlineRenderPipeline/Public/OutlineSubsystem.h
- Source/OutlineRenderPipeline/Private/OutlineSubsystem.cpp
- Source/OutlineRenderPipeline/Public/OutlineControlConsoleActor.h
- Source/OutlineRenderPipeline/Private/OutlineControlConsoleActor.cpp
- Source/OutlineRenderPipeline/Public/OutlineViewExtension.h
- Source/OutlineRenderPipeline/Private/OutlineViewExtension.cpp
- Shaders/Private/Outline.usf
- Source/OutlineRenderPipeline/Public/OutlineRenderPipelineModule.h
- Source/OutlineRenderPipeline/Private/OutlineRenderPipelineModule.cpp
- Source/OutlineRenderPipeline/OutlineRenderPipeline.Build.cs
- OutlineRenderPipeline.uplugin
- パラメータ調整してみる
- おわり!!!
- 雑談
始まり
描画エンジニアになりたい方の入門編にもちょうどよいコンテンツ。
そう、アウトライン(輪郭線)
(尚主観。異論は認めよう。)
それを幾つかの実装方法で描いていきます。
アウトライン(輪郭線)とは
読んで字の如く。
描画まわりの専門用語が一切必要の無い優しい子です。
これ以上の説明はない。
こういう感じ
これはエンジン改造でバカバカに手を加えているので、これと同等の見た目は作れないです。
(まぁまぁ大変なのよ。我ながら自信作だけど。ポストだけなのに線綺麗でしょー。えっへん。)
注意点なのよ
筆者はエンジン改造特化型です。
故にプラグイン設計とそこまで親密な仲ではないのです。
ただ、エンジンを触っているとプラグインのインターフェースを見かけたり、覗いたりすることが割とあるので『あー、たまに見かけるあの子ね。』程度には知っています。
要はいつも以上に情報が適当です。
筆者のプラグインのお勉強も兼ねているので、そこんとこよろしくですわ。
プラグインを作る
FSceneViewExtensionBase
今回の主役です。厳密にはISceneViewExtensionですが。
超ざっくりですが、この子は描画機能のインターフェースを提供しています。
なので描画機能にアクセスしたい場合(パス挿入とか)は、この子を使うことになります。
今回はポストプロセスでアウトラインを描画したいので、PrePostProcessPass_RenderThreadを使います。
読んで字の如く、ポストプロセスの前にパスを挿入できます。
位置的にはココです。
DeferredShadingRenderer.cpp#L4404
この後にポストプロセスマテリアルやブルーム、被写界深度などが積まれていきます。
(エンジン改造するたびに見かけていたので、地味に使ってみたかった機能なんですよねー。)
超シンプルに作ってみた
パラメータとかは全部固定で一旦描画できる状態の最小構成で作ってみます。
なんせFSceneViewExtensionBaseを使うのが初めてなので、まずは動作することを確認したいのです。
各種説明は後ほどするので、とりあえずコード直張りでサクサク進めますね。
OutlineViewExtension.h / .cpp
OutlineViewExtension.h
#pragma once #include "CoreMinimal.h" #include "SceneViewExtension.h" /** * ポストプロセスなアウトライン描画機能 */ class OUTLINERENDERPIPELINE_API FOutlineViewExtension : public FSceneViewExtensionBase { public: FOutlineViewExtension(const FAutoRegister& AutoRegister); virtual ~FOutlineViewExtension() = default; virtual void SetupViewFamily(FSceneViewFamily& InViewFamily) override {} virtual void SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView) override {} virtual void BeginRenderViewFamily(FSceneViewFamily& InViewFamily) override {} /** * Called right before Post Processing rendering begins */ virtual void PrePostProcessPass_RenderThread(FRDGBuilder& GraphBuilder, const FSceneView& View, const FPostProcessingInputs& Inputs) override; };
OutlineViewExtension.cpp
#include "OutlineViewExtension.h" #include "PostProcess/PostProcessing.h" #include "PostProcess/PostProcessMaterialInputs.h" #include "ScenePrivate.h" #include "SystemTextures.h" #include "SceneTextureParameters.h" #include "DataDrivenShaderPlatformInfo.h" DECLARE_GPU_STAT(Outline); class FOutlinePS : public FGlobalShader { public: DECLARE_GLOBAL_SHADER(FOutlinePS); SHADER_USE_PARAMETER_STRUCT(FOutlinePS, FGlobalShader); BEGIN_SHADER_PARAMETER_STRUCT(FParameters, ) SHADER_PARAMETER_STRUCT_REF(FViewUniformShaderParameters, View) SHADER_PARAMETER_STRUCT_INCLUDE(FSceneTextureShaderParameters, SceneTextures) SHADER_PARAMETER(float, Radius) SHADER_PARAMETER(float, Bias) SHADER_PARAMETER(float, Intensity) SHADER_PARAMETER(FVector3f, Color) RENDER_TARGET_BINDING_SLOTS() END_SHADER_PARAMETER_STRUCT() static bool ShouldCompilePermutation(const FGlobalShaderPermutationParameters& Parameters) { return IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM5) || IsFeatureLevelSupported(Parameters.Platform, ERHIFeatureLevel::SM6); } }; IMPLEMENT_GLOBAL_SHADER(FOutlinePS, "/Plugin/OutlineRenderPipeline/Private/Outline.usf", "MainPS", SF_Pixel); FOutlineViewExtension::FOutlineViewExtension(const FAutoRegister& AutoRegister) : FSceneViewExtensionBase(AutoRegister) { } void FOutlineViewExtension::PrePostProcessPass_RenderThread(FRDGBuilder& GraphBuilder, const FSceneView& View, const FPostProcessingInputs& Inputs) { Inputs.Validate(); FScene* Scene = View.Family->Scene->GetRenderScene(); const FIntRect PrimaryViewRect = static_cast<const FViewInfo&>(View).ViewRect; // Scene color is updated incrementally through the post process pipeline. FScreenPassTexture SceneColor((*Inputs.SceneTextures)->SceneColorTexture, PrimaryViewRect); RDG_EVENT_SCOPE(GraphBuilder, "Outline"); RDG_GPU_STAT_SCOPE(GraphBuilder, Outline); const FScreenPassTextureViewport InputViewport(SceneColor); const FScreenPassTextureViewport OutputViewport(InputViewport); FRDGTextureRef OutputTexture; { FRDGTextureDesc OutputTextureDesc = SceneColor.Texture->Desc; OutputTextureDesc.Reset(); OutputTextureDesc.Flags |= TexCreate_RenderTargetable | TexCreate_ShaderResource; OutputTexture = GraphBuilder.CreateTexture(OutputTextureDesc, TEXT("Outline.Output")); } // Outline Pass { TShaderMapRef<FScreenVS> VertexShader(static_cast<const FViewInfo&>(View).ShaderMap); TShaderMapRef<FOutlinePS> PixelShader(static_cast<const FViewInfo&>(View).ShaderMap); FOutlinePS::FParameters* PassParameters = GraphBuilder.AllocParameters<FOutlinePS::FParameters>(); PassParameters->View = View.ViewUniformBuffer; PassParameters->SceneTextures = GetSceneTextureShaderParameters(Inputs.SceneTextures); PassParameters->Radius = 1.0f; PassParameters->Bias = 2.0f; PassParameters->Intensity = 1.0f; PassParameters->Color = FVector3f::ZeroVector; PassParameters->RenderTargets[0] = FRenderTargetBinding(OutputTexture, ERenderTargetLoadAction::EClear); const FScreenPassTexture BlackDummy(GSystemTextures.GetBlackDummy(GraphBuilder)); // This gets passed in whether or not it's used. GraphBuilder.RemoveUnusedTextureWarning(BlackDummy.Texture); AddDrawScreenPass( GraphBuilder, RDG_EVENT_NAME("Outline"), View, OutputViewport, InputViewport, VertexShader, PixelShader, TStaticBlendState<>::GetRHI(), TStaticDepthStencilState<false, CF_Always>::GetRHI(), PassParameters); } // Copy Pass { AddCopyTexturePass(GraphBuilder, OutputTexture, SceneColor.Texture); } }
OutlineRenderPipelineModule.h / .cpp
プラグイン作成した際に自動生成されるファイルです。
初期の名前はOutlineRenderPipelineだと思います。
OutlineRenderPipelineModuleに名前変更しました。
OutlineRenderPipelineModule.h
#pragma once #include "CoreMinimal.h" #include "Modules/ModuleManager.h" class FOutlineViewExtension; class FOutlineRenderPipelineModule : public IModuleInterface { public: virtual void StartupModule() override; virtual void ShutdownModule() override; private: void OnPostEngineInit(); private: TSharedPtr<FOutlineViewExtension, ESPMode::ThreadSafe> ViewExtension; };
OutlineRenderPipelineModule.cpp
#include "OutlineRenderPipelineModule.h" #include "ShaderCore.h" #include "Interfaces/IPluginManager.h" #include "Misc/Paths.h" #include "Modules/ModuleManager.h" #include "OutlineViewExtension.h" #define LOCTEXT_NAMESPACE "FOutlineRenderPipelineModule" void FOutlineRenderPipelineModule::StartupModule() { FString PluginShaderDir = FPaths::Combine(IPluginManager::Get().FindPlugin(TEXT("OutlineRenderPipeline"))->GetBaseDir(), TEXT("Shaders")); AddShaderSourceDirectoryMapping(TEXT("/Plugin/OutlineRenderPipeline"), PluginShaderDir); FCoreDelegates::OnPostEngineInit.AddRaw(this, &FOutlineRenderPipelineModule::OnPostEngineInit); } void FOutlineRenderPipelineModule::ShutdownModule() { ViewExtension.Reset(); } void FOutlineRenderPipelineModule::OnPostEngineInit() { ViewExtension = FSceneViewExtensions::NewExtension<FOutlineViewExtension>(); } #undef LOCTEXT_NAMESPACE IMPLEMENT_MODULE(FOutlineRenderPipelineModule, OutlineRenderPipelineModule)
Plugins/OutlineRenderPipeline/Shaders/Private
Outline.usf
#include "/Engine/Private/Common.ush" #include "/Engine/Private/SceneTexturesCommon.ush" #include "/Engine/Private/DeferredShadingCommon.ush" static const uint kSampleCount = 4; static const float2 kSampleOffsetArray[kSampleCount] = { float2( 0.0, -1.0), float2( 1.0, 0.0), float2( 0.0, 1.0), float2(-1.0, 0.0), }; float Radius; float Bias; float Intensity; float3 Color; void MainPS( FScreenVertexOutput Input, out float4 OutColor : SV_Target0) { float2 BufferUV = Input.UV; float2 ClampedUV = clamp(BufferUV, View.BufferBilinearUVMinMax.xy, View.BufferBilinearUVMinMax.zw); float Weight = 0.0; float SceneDepth = CalcSceneDepth(ClampedUV); float NearSceneDepth = SceneDepth; UNROLL for (uint i = 0; i < kSampleCount; ++i) { float2 NewBufferUV = BufferUV + ViewportUVToBufferUV(Radius * kSampleOffsetArray[i] * View.ViewSizeAndInvSize.zw); float2 NewClampedUV = clamp(NewBufferUV, View.BufferBilinearUVMinMax.xy, View.BufferBilinearUVMinMax.zw); float NewSceneDepth = CalcSceneDepth(NewClampedUV); Weight += SceneDepth - NewSceneDepth; NearSceneDepth = min(NewSceneDepth, NearSceneDepth); } float3 SceneColor = CalcSceneColor(ClampedUV); float SkyMask = step(NearSceneDepth, 1000000.0); float Outline = saturate(PositiveClampedPow(Weight, Bias) * Intensity) * SkyMask; float3 Output = lerp(SceneColor, Color, Outline); OutColor = float4(Output, 1.0); }
OutlineRenderPipeline.uplugin
LoadingPhaseをDefaultからPostConfigInitに変更します。
{ "Modules": [ { "Name": "OutlineRenderPipeline", "Type": "Runtime", "LoadingPhase": "PostConfigInit" } ] }
できた!!!
エンジンのインターフェースなだけあって、描画まわりの実装は流用できますね。
SceneColorはSceneTexturesで所有しているので念のため出力先のバッファを別にして後でコピーする方法にしています。
描画パス位置も想定通りですね。
エンジンと同じように仕込んだStatも使えると。
ひと区切り
描画パスの挿入が出来ることが分かり、最低限の満足度は得られました。
意外といいですね。
ここまで出来るならエンジン改造ではインターフェースを用意して、実装はプラグインに逃がしてあげた方が開発イテレーション良いかもしれませんね。
実装
さて、この後は私が飽きるまで関数とか仕様の説明を、パラメータを流し込む部分を作りながら進めていきます。
流れはこんな感じです。
- アウトラインの設定を纏めた構造体を作成
- アウトラインの設定をWorldSubsystemでやり取り
- アウトラインの設定を操作する適当なアクターを作成
- アウトラインパスとシェーダーの作成
Source/OutlineRenderPipeline/Public/OutlineSettings.h
輪郭線のパラメータは構造体に纏めておきます。
エンジンのポストプロセス設定と同じスタイルです。
個別に実装しちゃうと、パラメータをやり取りする際に怠いことになるので、纏めています。
- アクターのプロパティから設定を書き換えて
- それをサブシステムに渡して
- 描画機能ではサブシステムから設定を取り出す
editcondition
を指定するとパラメータの有効性をチェックボックスで操作できるようになります。
後述するパラメータの上書き実装もラクにできるので、結構おすすめです。
Source/OutlineRenderPipeline/Private/OutlineSubsystem.cpp
普通に描画機能の実体を作成したり、アクセサを用意したりです。
OverrideOutlineSettings
は、マクロでコーディングをサボっています。
Source/OutlineRenderPipeline/Public/OutlineControlConsoleActor.h
輪郭線の設定をプロパティからするためだけに用意したアクタークラスです。
他のやり方をシンプルに知らないというクソザコです。
Source/OutlineRenderPipeline/Private/OutlineControlConsoleActor.cpp
エディタ上でパラメータ調整した際はOnConstructionのイベントで変更をサブシステムに伝達、ゲーム時はBeginPlayで伝達といった感じです。
Source/OutlineRenderPipeline/Public/OutlineViewExtension.h
SetupViewFamilyとSetupViewとBeginRenderViewFamilyは抽象型なので使わなくてもこんな感じで書いておいてください。書かないと当然、怒られます。
パラメータのやりとりの理想は、取得をGameThreadのSetupで行って、取得内容を元にRenderThreadでアレコレです。UEは基本的にスレッドセーフな設計なので雑に書いても大丈夫ですが、描画まわりに関しては例外的なことがあるので、若干注意です。しかもその例外ケースが開発プラットフォームによって発生したりするのがまたネックなポイント。今回は触り程度なのでこの辺り、結構適当です。なので危ないことしてるかも。
Source/OutlineRenderPipeline/Private/OutlineViewExtension.cpp
さぁ、メインの区間がやってきました。
端から端まで説明すると途中で飽きそうですが、とりあえず飽きるまではやってみます。
地味にいっつも説明省いてたのよね。面倒だから。
シンプルなシェーダーのフォーマットです。
UEのマテリアルを使用したい場合はFGlobalShader以外を継承する必要があるのですが、ポストエフェクトや計算用のコンピュートシェーダー系は、FGlobalShaderを使えば大体イケます。
ShouldCompilePermutation
は、このシェーダーを使えるプラットフォームとかに縛りを入れられるやつです。
今回だとShadingModel5とShadingModel6だけ許可しています。つまりPC版なら大体おっけい、OpenGLやMaliのモバイル系は怪しいといった感じです。
シェーダーで扱えるパラメータは、BEGIN_SHADER_PARAMETER_STRUCT
からEND_SHADER_PARAMETER_STRUCT
の間で宣言します。
View
は、ビューポートサイズやバッファサイズ、フレームインデックスとか、汎用的なパラメータが入っています。
SceneTexture
は、GBufferとSceneColorとSceneDepth(DeviceDepth)とか、バッファ系が全部入っています。地味に注意な点が、SceneColorも入っているので、RTとしてSceneColorを使用する場合は重複する危険性があります。ので今回は出力用のテクスチャを作成して、最後にコピーパス挿入といったフローを取っています。
残りは普通のパラメータですね。
ちょっと詳しい方なら『float4で纏めなくていいの?』と思うことでしょう。
ふふふ。UEさん、その辺りはすごいんですよ。内部でパディングを合わせてくれています。むしろ人間様が勝手にレイアウト調整すると無駄が発生するので、全任せするべきです。この辺りは商業エンジンの強みというか、凄いなぁと感じますね。
上記で実装した子のシェーダーファイルのパス指定とエントリーポイントの指定、シェーダーの種類を指定しています。
FOutlinePSは/Plugin/OutlineRenderPipeline/Private/Outline.usf
にあって、エントリーポイントはMainPSで、ピクセルシェーダーですよー、って感じ。
サブシステムからパラメータを取り出す必要があるので、サブシステムの有効性チェックと輪郭線の有効性チェックをしています。
このスコープ内で発生する描画イベントはOutlineというグループで区切りますよ、という感じ。
RDG_GPU_STAT_SCOPEはご存じStatです。
前述の通りSceneTexturesにSceneColorが入っていてRenderTargetに指定すると重複状態になるので、SceneColorと同じバッファサイズで出力用のテクスチャを作っています。
厳密に言えばSceneTexturesに含まれていても、シェーダー側で宣言しなければ問題なかったはずだけど、今回はSceneColor使いたかったのでどのみちです。
コピーコストの無駄を考えるとブレンドモードでやりくりするべきですけど、今回は最適化が主軸ではないので見逃してくださいな。
ちなみにテクスチャ名の付け方の規則として、その区間の名前ドット(.)なになに、がエンジンでは一般的です。
フルスクリーンシェーダーを作りたい場合の頂点シェーダーのプリセットがFScreenVSです。
後のシェーダーファイル区間で触れますが、ビューポート座標からバッファ座標に変換した値を返してくれたりとか、基本的な変換を済ませてくれる都合のいいヤツです。
先ほど宣言したシェーダーパラメータに値を詰めている箇所です。
アウトプットテクスチャをレンダーターゲットの0番目として指定している箇所です。
LoadActionにClearを指定しているので、クリアしてから書き込みが行われます。
クリア値は先ほどのOutputTextureDescにClearValueという要素があるので、そこで指定できます。
今回はSceneColorのクリア値をそのまま流用しているので、たぶんブラックです。
このパスで使用しないテクスチャをBlackDummyと呼ばれる真っ黒な適当なテクスチャに置き換えている箇所です。
テクスチャスロットに前回使ったものがそのままセットされているからそれを上書きとかだったかな、これはおまじない的なアレなので、あんまり意味は深くまで追っていないです。
パスを積んでいる箇所です。
ブレンドモードの指定をしたり、深度とステンシルの設定、入出力のビューポートサイズだったり、色々ですね。
OutputTextureに書き込まれた結果をSceneColorにコピーしている箇所です。
1枚コピーするだけならこの関数を使うのがラクです。
複数枚ある場合は自分でシェーダー作ってRT複数一気に書き込むといいでしょう。
ちょっと巻き戻る。
OutputTextureは書き込み先であるレンダーターゲットに指定したり、コピーパスでフェッチしたりするから、TexCreate_RenderTargetable
とTexCreate_ShaderResource
の両方指定が必要です。
仮に忘れても親切設計で実行時にフラグ指定よろしく♪とエラーの案内出るから説明不要かもだけど一応。
Shaders/Private/Outline.usf
汎用関数とSceneTexturesで使用できる各種テクスチャとサンプラの宣言がされているushをインクルードしています。
パラメータ宣言のシェーダー側ですね。
たしか変数名で判別していたので、順番は不定でも大丈夫なはずです。
可読性の観点から普通は合わせるけどね。
FScreenVertexOutput
から取得するUVはバッファ座標です。
ビューポート座標じゃないので注意です。
クランプする座標は端っこから1ピクセル内側にしています。
これは輪郭線の書き方次第なので、必要に応じて変えればいいと思います。
SceneDepthを取ってきてくれています。
デバイス深度が欲しい場合は、LookupDeviceZ
かSceneDepthTexture
を直に使ってフェッチしてください。
ここバッファとビューポート座標混在しているので注意です。
オフセット幅はビューポート基準で計算しているので変換を挟んでいて、基準となる座標はバッファ基準だから変換不要です。
オフセット幅の変換をサボると画面サイズが変わるたびに輪郭線の太さが変わるというバグが起きるので、変換忘れずにです。
私の環境だとSceneColorのAlphaを見ることないから1.0固定だけど、使う場合はちゃんとSceneColorを継続にしてあげてください。
CalcSceneColor
はRGBなので、CalcFullSceneColor
でRGBA取ってきてください。
Source/OutlineRenderPipeline/Public/OutlineRenderPipelineModule.h
描画機能の所有権をサブシステムに譲渡したので、中身すっからかんのモジュールちゃんです。
Source/OutlineRenderPipeline/Private/OutlineRenderPipelineModule.cpp
特筆すべき点は、シェーダーフォルダを追加しているぐらいですね。
Source/OutlineRenderPipeline/OutlineRenderPipeline.Build.cs
モジュールの依存関係部分ですね。
#include "PostProcess/PostProcessing.h"
とあともう1つはゴメン忘れた、これはRendererフォルダに入っているヤツなので、Path.Combine(GetModuleDirectory("Renderer"), "Private")
を追加しないとダメです。
OutlineRenderPipeline.uplugin
シェーダーの読込が発生するため、LoadingPhaseをDefaultからPostConfigInitに変更しています。
なので理想はシェーダーまわりとランタイムでモジュール分割して、読込タイミングをそれぞれにするべきです。今回は面倒ね。
パラメータ調整してみる
おわり!!!
プラグインで描画機能にアクセスするの検証以外では久々だったので結構楽しめました。
パラメータのやり取りをもう少しスマートに出来たらより楽しかったですね。
実装はサブモジュールで見れるはずです。たぶん。
ん-、疲れた。
次回はエンジン改造編、と言いたいところですが、せっかくやるならアウトラインマスクをしたいですね。
ちょっと遠回りになりますが、間に3本挟みます。
- シェーディングモデルの追加
- アウトラインマスクを格納するマテリアルピンの追加
- 空いているGBufferにアウトラインマスクを格納する
この前提実装を終えてようやくアウトライン エンジン改造編に突入です。
恋愛アドベンチャーゲームに比べたら少ない選択肢ですけどねー。
雑談
ここから先は年齢制限のかかっている作品を取り扱うページとなります。
表示する
目次
- ユイちゃんが悪い顔して悪いことしてる!!! - 2024/08/12 (月)
- アクリルジオラマ付メロンブックス限定版 - 2024/08/12 (月)
- 抱き枕の本体をいつか買いたいかも - 2024/08/12 (月)
- シーラブを遊ぶ宣言 - 2024/08/12 (月)
- 5本ぐらい記事を書きたい気持ち - 2024/08/13 (火)
- 履行失敗 - 2024/08/17 (土)
ユイちゃんが悪い顔して悪いことしてる!!! - 2024/08/12 (月)
ギャラリーの2列目の5行目のCG絵です。
ああああああああああああああああああ。
みなt、じゃなくて、ユイちゃん!!
そんな悪そうな表情するんですね!!
素敵です!!
既視感あるなと思ったら湊くんと風莉さんも夜の白鈴学園で窓(夜空)を背景にえっちしてましたね。
この赤枠で囲んだシーンのことね。
ぱれっとは利用規約バカ厳しいので全モザで保険。というか厳密にはこれも確かダメなんだっけなぁ。
一応非営利ブログという最終保険があるので、法廷で合うことになっても、微々たる金額で済むことを願いたい。
アクリルジオラマ付メロンブックス限定版 - 2024/08/12 (月)
う~む。
買おうと思えば当然変えるけど、流石に要らねぇかなぁ。
いや欲しいんだけど、コスパがなぁ。
好きなものにコスパを求めるのは、愛が弱いと思いますが、労働力という対価を払って得た微々たる報酬なので、使える額に限りがあるんですよ。
発売まで数か月もあるので、気が変わることもあるでしょう。
そしたらその日の気分に従って散財しましょうね。ねぇ~。わたし~。
抱き枕の本体をいつか買いたいかも - 2024/08/12 (月)
お引越ししたら買いたいなと思っているわたくし。
https://www.a-and-j.co.jp/item/cate13/20181105-46/
ず~っとブックマークに居座り続けている抱き枕本体ちゃん。
シーラブを遊ぶ宣言 - 2024/08/12 (月)
3連休という絶好の機会をマジで度忘れしてしまった罪人の筆者さん。
コード書くのは結構なことですが、それよりも遊んでください。いい加減に。
せっかく買ったんだから。ねぇ?
そうね、来週というか今週は、プラグイン編を書くことでしょうから、その時に読み上げツールを片手に作業しましょう。
ということで未来の予約をしておきます。
さぁ、書いたんだから、やれよな。わたし。
んじゃ、今日の私はこれでお休み。
あ、待って。
やるべきことが落ち着いたらこれ見といて、わたし。
よし、おやすみ。
5本ぐらい記事を書きたい気持ち - 2024/08/13 (火)
現状は5本構成ですわ。
ブログを書きたい欲求が大爆発中なの。
あるよね、欲を抑えられない時。
履行失敗 - 2024/08/17 (土)
文章を書きながらエロゲを遊べますか?
答えはノーですわ。
まぁいいですわ。
今はエロゲ欲よりもブログ欲が勝っているの。
この欲がいつまで持つか知らんけど、欲求には素直に従うべきと思う筆者さん、直近の攻略は諦めました。
というかセレオブの脳内再生機能が暫く動作する関係で、新規プレイで物語を取り込んじゃうと多分記憶パンクしちゃう。
筆者さん現実世界の人間が関連する記憶力はゴミカス過ぎるんだけど、コーディングとエロゲに関しては割と誇れる。
えっへん。
買うかも買わんかも - 2024/08/17 (土)
組み合わせのメモ。
ソフマップは特別限定版。
Amazonは通常版、絵がめっちゃエッチで好き。
駿河屋は通常版、構図が好き、ただ駿河屋は発売日にどうせ届かないから、ココ単体で買うのは絶対ダメ。
大体4万くらい。
オトメきの店舗特典次第なところだから、保留かな。
ただなー、別に、タペストリー買ったところで、飾らんのよな。
なんのために買ってるんだろう。
まー好きなことに対して意味を問いて合理性を持たせたところでしょうもないので、別にいいか。
理由なんて好きだから&趣味の一言で片付くことだし。