Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
『GRANBLUE FANTASY: Relink』の長期開発を支え続けたテスト自動化の取り組み紹介
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
Cygames, Inc.
PRO
December 18, 2024
Technology
670
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.
【CEDEC2026】Creative Approaches to Localizing the Dialects and Unique Speech of Umamusume: Pretty Derby Characters in English
cygames
PRO
4
27k
【CEDEC2026】コードレビュー支援ツール開発から学ぶ:LLMを用いた業務システムの実践的な運用設計と誤出力対策
cygames
PRO
0
1.2k
【CEDEC2026】『ウマ娘 プリティーダービー』 英語版のキャラクターの方言や口調をローカライズするための創造的アプローチ
cygames
PRO
3
1.3k
【CEDEC2026】次世代デジタルカードゲームのサーバー設計と運用 〜『Shadowverse: Worlds Beyond』の舞台裏~
cygames
PRO
2
2.1k
【CEDEC2026】派手さと見やすさは両立できる!『Shadowverse: Worlds Beyond』エフェクト・3D背景の超進化したビジュアル設計
cygames
PRO
1
1.9k
【CEDEC2026】『Shadowverse: Worlds Beyond』続編開発におけるUIデザイン・アニメーション演出の「超進化」 ~継承と挑戦を両立するデザイン手法~
cygames
PRO
3
5.2k
【CEDEC2026】『Relink』を拡張せよ - 『GRANBLUE FANTASY: Relink - Endless Ragnarok』の開発速度と品質を守るCI運用
cygames
PRO
1
280
【CEDEC2026】『GRANBLUE FANTASY: Relink - Endless Ragnarok』のバトル制作事例 ~最高のキャラゲーを目指して~
cygames
PRO
2
490
【CEDEC2026】アートとゲームデザインをつなぐVFX設計『GRANBLUE FANTASY: Relink - Endless Ragnarok』における表現と可読性の両立
cygames
PRO
1
1.3k
Other Decks in Technology
See All in Technology
beyond jj: config & tools ecosystem
indirect
0
430
MCPをつなげて作る組織横断のAIエージェント基盤(の開発工程)
tsubakimoto_s
0
130
負債のメタファと2026年 / Debt Metaphor in Agentic Engineering Age 202609 Edition
twada
PRO
5
2.7k
iOSDC Japan 2026 day1 TrackC 10:50
feedtailor
1
160
Claude Code本って、 読む必要あるの?
oikon48
2
460
目の前の楽しいが人生を変える - コミュニティの螺旋の歩き方と楽しむコツ / change your life
soudai
PRO
4
610
アクセスキーこわい やめかたと漏らさない工夫
sassssan68
1
240
AI に書かせたその API、 “信頼” できますか?
nagix
0
110
OpenTelemetryのメトリクスをCloudWatchに送ってPromQLで見てみた
ota1022
0
150
AI de Idea
kawaguti
PRO
2
110
データ界隈LT祭 第1回LT登壇
taromatsui_cccmkhd
1
1.3k
安心して変更できるWebフロントエンドの作り方
pirosikick
4
2.5k
Featured
See All Featured
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.6k
Scaling GitHub
holman
464
140k
The Illustrated Guide to Node.js - THAT Conference 2024
reverentgeek
1
490
AI in Enterprises - Java and Open Source to the Rescue
ivargrimstad
0
1.5k
Designing Experiences People Love
moore
143
24k
Build your cross-platform service in a week with App Engine
jlugia
234
19k
Balancing Empowerment & Direction
lara
6
1.3k
Side Projects
sachag
456
43k
Ruling the World: When Life Gets Gamed
codingconduct
0
330
The Cost Of JavaScript in 2023
addyosmani
55
10k
BBQ
matthewcrist
89
10k
Discover your Explorer Soul
emna__ayadi
2
1.3k
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