Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
『GRANBLUE FANTASY: Relink』の長期開発を支え続けたテスト自動化の取り組み紹介
Search
Cygames, Inc.
PRO
December 18, 2024
Technology
650
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
『GRANBLUE FANTASY: Relink』の長期開発を支え続けたテスト自動化の取り組み紹介
Cygames, Inc.
PRO
December 18, 2024
More Decks by Cygames, Inc.
See All by Cygames, Inc.
【U/Day Tokyo 2025】Cygames流 最新スマートフォンゲームの技術設計 〜『Shadowverse: Worlds Beyond』におけるアーキテクチャ再設計の挑戦~
cygames
PRO
4
17k
【CEDEC+KYUSHU2025】学生・若手必見!テクニカルアーティスト 大全 ~仕事・スキル・キャリアパス、TAの「わからない」を徹底解剖~
cygames
PRO
1
1.3k
【TiDB User Day2025】リリース時のアクセス急増をいかにしてノーメンテで乗り越えたか 〜『Shadowverse: Worlds Beyond』におけるTiDB採用のゲームサーバー設計〜
cygames
PRO
1
2.9k
【CEDEC2025】『Shadowverse: Worlds Beyond』二度目のDCG開発でゲームをリデザインする~遊びやすさと競技性の両立~
cygames
PRO
2
980
【CEDEC2025】大規模言語モデルを活用したゲーム内会話パートのスクリプト作成支援への取り組み
cygames
PRO
2
2.7k
【CEDEC2025】現場を理解して実現!ゲーム開発を効率化するWebサービスの開発と、利用促進のための継続的な改善
cygames
PRO
0
1.9k
【CEDEC2025】ブランド力アップのためのコンテンツマーケティング~ゲーム会社における情報資産の活かし方~
cygames
PRO
0
2k
【CEDEC2025】『ウマ娘 プリティーダービー』における映像制作のさらなる高品質化へ!~ 豊富な素材出力と制作フローの改善を実現するツールについて~
cygames
PRO
0
860
【CEDEC2025】LLMを活用したゲーム開発支援と、生成AIの利活用を進める組織的な取り組み
cygames
PRO
1
5.3k
Other Decks in Technology
See All in Technology
AI時代の開発生産性は、個人技からチーム設計へ
moongift
PRO
4
2.3k
Claude Code公式skillで 自分の仕事を少しずつ手放そう!(Claude Code開発ノウハウ大公開スペシャル by クラスメソッド)
kaym
1
460
【Claude Code】鹿野さんに聞く 私の推しの並行開発環境 大公開 / claude-code-parallel-2026-07-15
tonkotsuboy_com
12
8.5k
SoccerMaster: A Vision Foundation Model for Soccer Understanding
kzykmyzw
0
130
End-to-Endで考える信頼性 —LINEアプリにおけるクライアント開発×SRE連携の実践
maruloop
4
4.5k
個人開発で育てる「大規模設計の苗床」 - AI時代の1人開発から始める業務への知識接続 / The Seedbed for Large-Scale Design - From AI-Era Solo Projects to Professional Knowledge
bitkey
PRO
1
270
Alphaモジュール使っていいのかい!?いけないのかい!?どっちなんだいっ!?
watany
1
260
Control Planeで育てるBtoB SaaSの認証基盤 - SRE NEXT 2026
pokohide
1
2.5k
SRE本の知られざる名シーン / The Hidden Gems of Google SRE Book
nari_ex
1
420
「AIに依存している」と 「AIを使いこなしている」の違い
k8yasuma
0
110
Amplify Gen2でbackend.tsにCDKを定義する/しない事によるCDKの挙動の違いとユースケース
smt7174
1
350
なぜ私たちのSREプラクティスはなかなか機能しないのか 〜システムより先に組織を見る〜 / Why our SRE practices aren't really working
vtryo
4
3.9k
Featured
See All Featured
The Limits of Empathy - UXLibs8
cassininazir
1
490
Have SEOs Ruined the Internet? - User Awareness of SEO in 2025
akashhashmi
0
390
XXLCSS - How to scale CSS and keep your sanity
sugarenia
249
1.3M
RailsConf 2023
tenderlove
30
1.5k
Typedesign – Prime Four
hannesfritz
42
3.1k
Abbi's Birthday
coloredviolet
3
8.7k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
49
10k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
Making Projects Easy
brettharned
120
6.7k
From Legacy to Launchpad: Building Startup-Ready Communities
dugsong
0
260
Facilitating Awesome Meetings
lara
57
7k
The Cult of Friendly URLs
andyhume
79
6.9k
Transcript
1/92
2/92
3/92 ◆ ◆ ◆
4/92
5/92
6/92
7/92
8/92
9/92
10/92
11/92
12/92
13/92
14/92
15/92
16/92 ◆ ◆
17/92
18/92 ◆ ◆ ◆
19/92
20/92 autoplay_pipeline.exe ( golang ) Jenkins ( groovy Pipeline Script
) granblue_fantasy_relink.exe
21/92
22/92 ◆ ◆ ◆
23/92
24/92
25/92
26/92
27/92
28/92
29/92
30/92
31/92
32/92
33/92
34/92
35/92
36/92
37/92
38/92
39/92
40/92
41/92
42/92
43/92 ◆ ◆
44/92
45/92
46/92
47/92
48/92
49/92
50/92
51/92
52/92
53/92
54/92
55/92 ◆ ◆ ◆
56/92
57/92
58/92
59/92
60/92 ◆ ◆ ◆ ◆
61/92
62/92 f32 PointLightData::updateDistanceCurve() { auto retRate = 0.0f; auto distanceMin
= distanceA_; auto distanceMax = distanceB_; auto t = (cameraDistance_ - distanceMin) / (distanceMax - distanceMin); t = GET_CLAMPF(t, 0.f, 1.f); // return t; }
63/92 f32 PointLightData::updateDistanceCurve() { auto retRate = 0.0f; auto distanceMin
= distanceA_; auto distanceMax = distanceB_; // . auto denominator = (distanceMax - distanceMin); auto t = denominator <= FLT_EPSILON ? 1.f : (cameraDistance_ - distanceMin) / denominator; t = GET_CLAMPF(t, 0.f, 1.f); // return t; }
64/92
65/92 ◆ ◆ ◆ ◆
66/92 Matrix44 transform[3]; // Vector4 vertexs[16]; for (s32 i =
0, inum = std::size(vertexs); i < inum; ++i) { const div_t d = div(i, 4); VecMulMtx(vertexs[i], g_baseVertex[d.rem], transform[d.quot]); }
67/92 Matrix44 transform[4]; // Vector4 vertexs[16]; for (s32 i =
0, inum = std::size(vertexs); i < inum; ++i) { const div_t d = div(i, 4); VecMulMtx(vertexs[i], g_baseVertex[d.rem], transform[d.quot]); }
68/92 constexpr MaterialVariationID DEFAULT_MATERIAL_VARIATION_ID = 0; void hoge() { huga
= KeyData::getConstantKeyParamWithDefault(subseq.materialVariationIdList_, 0.0f, asset::DEFAULT_MATERIAL_VARIATION_ID); } template<typename KeyParam> KeyParam getConstantKeyParamWithDefault(const std::vector<Timeline::EventKeyParamData<KeyParam>>& list, f64 nowSeconds, const KeyParam& defaultValue) { // }
69/92 constexpr MaterialVariationID DEFAULT_MATERIAL_VARIATION_ID = 0; void hoge() { huga
= KeyData::getConstantKeyParamWithDefault(subseq.materialVariationIdList_, 0.0f, asset::DEFAULT_MATERIAL_VARIATION_ID); } template<typename KeyParam> KeyParam getConstantKeyParamWithDefault(const std::vector<Timeline::EventKeyParamData<KeyParam>>& list, f64 nowSeconds, const KeyParam defaultValue) { // }
70/92 SceneObject* SceneObject::clone(SceneObject* pParent, bool isRename, bool isEntity) const {
const char* pName; if (isRename != false) { auto name = mName; // pName = name.get(); } else { pName = mName.get(); } auto pNewObject = SceneObject::create(className(), pName, pParent, false); }
71/92 SceneObject* SceneObject::clone(SceneObject* pParent, bool isRename, bool isEntity) const {
SceneObject* pNewObject = nullptr; if (isRename != false) { auto name = name_; // pNewObject = SceneObject::create(className(), name.get(), pParent, false); } else { pNewObject = SceneObject::create(className(), mName.get(), pParent, false); } }
72/92
73/92
74/92
75/92
76/92 void write() { // void* address = reinterpret_cast<void*> (0x0123456789A);
// placement new MyClass* obj = new (address) MyClass(42); } void read() { // void* address = reinterpret_cast<void*> (0x0123456789A); std::cout << reinterpret_cast<MyClass*> (address)->data << std::endl; }
77/92 ================== WARNING: ThreadSanitizer: data race (pid=174) Write of size
8 at 0x0010a48bb360 by thread T11: #0 0x81119cf39 (ThreadSanitizer.sprx+0x64f39) #1 0x26670e46 in Mtx44Copy(Matrix44&, Matrix44 const&) { } #2 0x26670e46 in Matrix44::operator=(Matrix44 const&) { } #3 0x2c23d8b8 in Joint::setWorldMatrix(Matrix44 const&) { } #4 0x2c23d8b8 in JointList::addVecJointMatrix(Joint&, Vector4 const&) { } … Previous read of size 8 at 0x0010a48bb360 by thread T12: )(void*, void*) const { } … #5 0x2c23b98e in Joint::getWorldPos() const { } #6 0x2c22ce88 in ModelImpl::getWorldPos() const { } …
78/92 ================== WARNING: ThreadSanitizer: data race (pid=174) Write of size
8 at 0x0010a48bb360 by thread T11: #0 0x81119cf39 (ThreadSanitizer.sprx+0x64f39) #1 0x26670e46 in Mtx44Copy(Matrix44&, Matrix44 const&) { } #2 0x26670e46 in Matrix44::operator=(Matrix44 const&) { } #3 0x2c23d8b8 in Joint::setWorldMatrix(Matrix44 const&) { } #4 0x2c23d8b8 in JointList::addVecJointMatrix(Joint&, Vector4 const&) { } … Previous read of size 8 at 0x0010a48bb360 by thread T12: )(void*, void*) const { } … #5 0x2c23b98e in Joint::getWorldPos() const { } #6 0x2c22ce88 in ModelImpl::getWorldPos() const { } …
79/92 void Np0100::updateParameter() { Vector4 playerPos = PlayerManager::GetManualPlayerPos(); Vector4 vec
= getPos() - playerPos; f32 length = VecMag(vec); f32 lengthY = vec.y; vec.y = 0.0f; f32 lengthXZ = VecMag(vec); }
80/92 void Np0100::updateParameter() { Vector4 playerPos = PlayerManager::GetManualPlayerPosRef(); Vector4 vec
= getPos() - playerPos; f32 length = VecMag(vec); f32 lengthY = vec.y; vec.y = 0.0f; f32 lengthXZ = VecMag(vec); }
81/92 ================== WARNING: ThreadSanitizer: data race (pid=490) Write of size
8 at 0x00101a3a4000 by thread T10: #0 0x73b877da in model::bool_vector::set(unsigned long, bool) {コードの場所} #1 0x73bbfcdf in model::BoundingVolume::setForceVisible(bool) const {コードの場所} #2 0x73bfc74b in ModelImpl::updateBoundingVolume() {コードの場所} #3 0x73c24800 in auto ModelProcessingImpl::updateModelList(concurrency::JobSystem&):: ...template... const {コード の場所} #4 0x73c24800 in void concurrency::JobSystem::parallelForEach ...template... const {コードの場所} Previous write of size 8 at 0x00101a3a4000 by thread T14: #0 0x73b877bc in model::bool_vector::set(unsigned long, bool) {コードの場所} #1 0x73bbfcdf in model::BoundingVolume::setForceVisible(bool) const {コードの場所} #2 0x73bfc74b in ModelImpl::updateBoundingVolume() {コードの場所} #3 0x73c24800 in auto ModelProcessingImpl::updateModelList(concurrency::JobSystem&):: ...template... const {コード の場所} #4 0x73c24800 in void concurrency::JobSystem::parallelForEach ...template... const {コードの場所}
82/92 int main() { std::vector<u8> numbers = { 1, 2,
3 }; std::thread t1([&]() { numbers[0] = 42; //OK }); std::thread t2([&]() { numbers[1] = 84; //OK }); t1.join(); t2.join(); return 0; } int main() { std::vector<u8> numbers = { 1, 2, 3 }; std::thread t1([&]() { numbers[0] = 42; //t2 }); std::thread t2([&]() { numbers[0] = 84; //t1 }); t1.join(); t2.join(); return 0; }
83/92 int main() { std::vector<bool> flags = { true, false,
true }; std::thread t1([&]() { flags[0] = true; //NG }); std::thread t2([&]() { flags[1] = true; //NG }); t1.join(); t2.join(); return 0; }
84/92
85/92
86/92
87/92 ◆ ◆ ◆
88/92
89/92 ◆ ◆ ◆
90/92 ◆ ◆ ◆
91/92 ◆ ◆ ◆
92/92