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
Cygames, Inc.
PRO
December 18, 2024
Technology
760
1
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.
【ゲームメーカーズスクランブル2026】『Shadowverse: Worlds Beyond』UIとアニメーションで実現する最高のユーザー体験を叶えるプロトタイピング
cygames
PRO
1
850
【CEDEC2026】Creative Approaches to Localizing the Dialects and Unique Speech of Umamusume: Pretty Derby Characters in English
cygames
PRO
5
28k
【CEDEC2026】コードレビュー支援ツール開発から学ぶ:LLMを用いた業務システムの実践的な運用設計と誤出力対策
cygames
PRO
1
1.4k
【CEDEC2026】『ウマ娘 プリティーダービー』 英語版のキャラクターの方言や口調をローカライズするための創造的アプローチ
cygames
PRO
4
1.4k
【CEDEC2026】次世代デジタルカードゲームのサーバー設計と運用 〜『Shadowverse: Worlds Beyond』の舞台裏~
cygames
PRO
3
2.2k
【CEDEC2026】派手さと見やすさは両立できる!『Shadowverse: Worlds Beyond』エフェクト・3D背景の超進化したビジュアル設計
cygames
PRO
2
2.2k
【CEDEC2026】『Shadowverse: Worlds Beyond』続編開発におけるUIデザイン・アニメーション演出の「超進化」 ~継承と挑戦を両立するデザイン手法~
cygames
PRO
4
5.4k
【CEDEC2026】『Relink』を拡張せよ - 『GRANBLUE FANTASY: Relink - Endless Ragnarok』の開発速度と品質を守るCI運用
cygames
PRO
2
470
【CEDEC2026】『GRANBLUE FANTASY: Relink - Endless Ragnarok』のバトル制作事例 ~最高のキャラゲーを目指して~
cygames
PRO
3
650
Other Decks in Technology
See All in Technology
ブラウザとサーバーから考えるAI時代のフロントエンド監視
shogo4131
3
910
1つのリクエストを追って理解する - llm-dのP/D分離
akhino
0
140
Optunaの最新リリースv5.0について
pfn
PRO
0
100
サブエージェントと Claude Mods で作る独自 Advisor ツールの話
kenichiodo
2
150
re:Invent初参加で変わったこと、2回目にやってみたいこと(JAWS-UG彩の国埼玉 #12)
mhrtech
2
130
ソフトウェア ファクトリー - 再現性のあるソフトウェアの工場づくり / Software factory
iktakahiro
5
4.5k
パッチ更新でterraform initが落ちた日
grimoh
1
140
Conditional PED-ANOVA: Hyperparameter Importance in Hierarchical & Dynamic Search Spaces
pfn
PRO
0
120
Benchmarking Disaggregated Serving on vLLM + Dynamo with a Real Coding-Agent Workload
nttcom
1
170
触りたくないデータに、どう触るか AI時代のRBACアクセスガバナンス
ivry_presentationmaterials
0
180
その Valkey 使いこなしていますか?
onk
PRO
5
1.1k
事業活動を AI Ready にする攻めと守りのデータエンジニアリング / data-engineering-for-ai-ready-business
pei0804
7
2.8k
Featured
See All Featured
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
47
8.4k
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
780
How to make the Groovebox
asonas
2
2.5k
Performance Is Good for Brains [We Love Speed 2024]
tammyeverts
12
1.9k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
230
23k
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
990
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.5k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
Amusing Abliteration
ianozsvald
1
340
It's Worth the Effort
3n
188
29k
Fashionably flexible responsive web design (full day workshop)
malarkey
409
67k
Design in an AI World
tapps
1
350
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