游戏必备组件有哪些_使用DOTS制作一款第三人称僵尸射击游戏

本文,瑞典游戏工作室Far North Entertainment将分享在传统的Unity项目中使用DOTS的经验。我们正在使用面向数据技术栈DOTS重构Unity的核心基础。许多游戏工作室在使用C# Job System、实体组件系统ECS和Burst Compiler后,都无一例外地感受到明显的性能提升,其中就包含了瑞典游戏工作室Far North Entertainment。在Un...
摘要由CSDN通过智能技术生成

本文,瑞典游戏工作室Far North Entertainment将分享在传统的Unity项目中使用DOTS的经验。

我们正在使用面向数据技术栈DOTS重构Unity的核心基础。许多游戏工作室在使用C# Job System、实体组件系统ECS和Burst Compiler后,都无一例外地感受到明显的性能提升,其中就包含了瑞典游戏工作室Far North Entertainment。

在Unite Copenhagen大会上,我们与Far North Entertainment工作室的成员进行深入交流,了解他们如何在传统的Unity项目中应用DOTS功能。

a99dffc6bd2c2b1063876922228be624.png

Far North Entertainment

瑞典的游戏工作室Far North Entertainment是由5位来自工程研究专业的好友共同创建。自2018年初在Gear VR平台发布《Down to Dungeon》游戏之后,该公司一直致力开发一款末日僵尸生存游戏。

这款末日僵尸生存游戏的独特之处在于僵尸的数量,开发团队希望实现成千上万个饥渴的僵尸追逐玩家的效果。然而在构建原型时,他们遇到了许多性能方面的问题。

开发中主要的瓶颈在于对庞大数量僵尸的进行生成、销毁、更新和添加动画,虽然开发团队尝试了对象池和动画实例化等方法,但效果仍不显著。因此,技术总监Anders Eriksson将目光投向DOTS,从面向对象(Object-oriented)设计转为面向数据(Data-oriented)设计。

Anders Eriksson表示:促成我们思维模式发生改变的关键是停止考虑对象和对象层级,转为思考数据是如何变换和访问的。这意味着代码不必围绕具体事物来编写,不用处理过去最常见的情况。

对于同样在试着转换思维模式的开发者,Anders Eriksson的建议是:先弄清楚要解决的问题和解决方案的相关数据。是否会对相同数据集执行相同的处理过程?可以把多少关联数据打包到CPU缓存行中?如果想转换现有代码的话,那么要确定会给缓存行加入的垃圾数据量。能否将运算过程分配到多个线程上,能否利用SIMD指令?

在进一步学习后,开发团队了解到Unity组件系统的实体只是组件流中的查找ID。组件只是数据,而系统包含了所有逻辑,系统会使用称为“Archetypes(原型)”的特别组件标识来过滤实体。

Anders Eriksson表示:我们将ECS看作SQL数据库可以帮助我们更好地理解它。每个Archetype原型是一张表格,每行代表一个组件,每列代表一个独特的实体。我们可以使用系统查询这些Archetype原型表,在实体上执行操作。

开始使用DOTS

为了更好地理解,Anders Eriksson研究了实体组件系统的文档和ECS示例项目,以及Unity与Nordeus合作制作的示例项目。

此外,关于面向数据设计的学习材料也对团队有很大的帮助。CppCon 2014大会上Mike Acton关于面向数据设计的演讲开阔了他们的眼界,让开发团队了解了这种编程方式。

Far North Entertainment的开发团队在博客上发表了许多学习心得,今年9月,他们在Unite Copenhagen大会上进行演讲,介绍了转换到面向数据思维的经验。

本文的内容将以这次演讲作为基础,并且详细地讲解该团队应用ECS、C# Job System和Burst Compiler的具体方法。

排列僵尸数据

Anders Eriksson表示:我们面临的主要问题是客户端的转换信息插入,以及对上千个实体的转向信息。

开发团队最初使用面向对象的方法,编写了ZombieView脚本的抽象,它继承了更为常用的EntityView父类。EntityView是附加在游戏对象的MonoBehaviour,它会用作游戏模型的可视化展示。每个ZombieView

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个简单的贪吃蛇游戏的Java Swing实现,希望能够帮到你: ```java import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.ArrayList; import java.util.Random; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.Timer; public class SnakeGame extends JPanel implements ActionListener, KeyListener { private static final long serialVersionUID = 1L; private static final int WIDTH = 500; private static final int HEIGHT = 500; private static final int DOT_SIZE = 10; private static final int ALL_DOTS = 900; private static final int RAND_POS = 29; private final int x[] = new int[ALL_DOTS]; private final int y[] = new int[ALL_DOTS]; private int dots; private int apple_x; private int apple_y; private boolean leftDirection = false; private boolean rightDirection = true; private boolean upDirection = false; private boolean downDirection = false; private boolean inGame = true; Timer timer; public SnakeGame() { addKeyListener(this); setPreferredSize(new Dimension(WIDTH, HEIGHT)); setBackground(Color.BLACK); setFocusable(true); initGame(); } public void initGame() { dots = 3; for (int z = 0; z < dots; z++) { x[z] = 50 - z * 10; y[z] = 50; } locateApple(); timer = new Timer(100, this); timer.start(); } public void paintComponent(Graphics g) { super.paintComponent(g); doDrawing(g); } public void doDrawing(Graphics g) { if (inGame) { g.setColor(Color.RED); g.fillOval(apple_x, apple_y, DOT_SIZE, DOT_SIZE); for (int z = 0; z < dots; z++) { if (z == 0) { g.setColor(Color.WHITE); g.fillRect(x[z], y[z], DOT_SIZE, DOT_SIZE); } else { g.setColor(Color.GREEN); g.fillRect(x[z], y[z], DOT_SIZE, DOT_SIZE); } } } else { gameOver(g); } } public void gameOver(Graphics g) { String msg = "Game Over"; g.setColor(Color.WHITE); g.drawString(msg, (WIDTH - 56) / 2, HEIGHT / 2); timer.stop(); } public void checkApple() { if ((x[0] == apple_x) && (y[0] == apple_y)) { dots++; locateApple(); } } public void move() { for (int z = dots; z > 0; z--) { x[z] = x[(z - 1)]; y[z] = y[(z - 1)]; } if (leftDirection) { x[0] -= DOT_SIZE; } if (rightDirection) { x[0] += DOT_SIZE; } if (upDirection) { y[0] -= DOT_SIZE; } if (downDirection) { y[0] += DOT_SIZE; } } public void checkCollision() { for (int z = dots; z > 0; z--) { if ((z > 4) && (x[0] == x[z]) && (y[0] == y[z])) { inGame = false; } } if (y[0] >= HEIGHT) { inGame = false; } if (y[0] < 0) { inGame = false; } if (x[0] >= WIDTH) { inGame = false; } if (x[0] < 0) { inGame = false; } if (!inGame) { timer.stop(); } } public void locateApple() { Random r = new Random(); apple_x = r.nextInt(RAND_POS) * DOT_SIZE; apple_y = r.nextInt(RAND_POS) * DOT_SIZE; } public void actionPerformed(ActionEvent e) { if (inGame) { checkApple(); checkCollision(); move(); } repaint(); } public void keyPressed(KeyEvent e) { int key = e.getKeyCode(); if ((key == KeyEvent.VK_LEFT) && (!rightDirection)) { leftDirection = true; upDirection = false; downDirection = false; } if ((key == KeyEvent.VK_RIGHT) && (!leftDirection)) { rightDirection = true; upDirection = false; downDirection = false; } if ((key == KeyEvent.VK_UP) && (!downDirection)) { upDirection = true; rightDirection = false; leftDirection = false; } if ((key == KeyEvent.VK_DOWN) && (!upDirection)) { downDirection = true; rightDirection = false; leftDirection = false; } } public void keyReleased(KeyEvent e) { } public void keyTyped(KeyEvent e) { } public static void main(String[] args) { JFrame frame = new JFrame("Snake"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(new SnakeGame()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } } ``` 以上是一个简单的贪吃蛇游戏的Java Swing实现,其中用到了Timer、JFrame、JPanel等组件和类,你可以根据需要进行修改和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值