用C++和SFML写游戏-纹理及精灵(5.2)

上一篇文章介绍了纹理的使用。这一节将会介绍 精灵 以及资源管理。

一、什么是精灵

在这之前你可能听说过精灵的概念。实际上,精灵是贴有纹理的“画布”。说到这里,有人可能会说,前面的 Shpae类不是也一样的吗?答案是的,但是精灵有一点不同。

最重要的不同点是,精灵总是绘制在纹理矩形中,而 Shape 可以不必这样,精灵总是与一个纹理关联的,它不能单独存在。除此之外, Sprite类有一个类似与 Shape::setFillColor()的函数 Sprite::setColor()。当它们都与一个纹理(Texture)相关联的时候,效果是一样的,但是如果 Sprite 没有与纹理(Texture)关联的话,它不会被指定的颜色绘制,相反的是 Shape 是可以被指定颜色绘制的。

那么问题来了?为什么我们要使用精灵而不是 Shape呢?从上面的描述来看,精灵似乎是弱化版的Shape。真正的原因是因为精灵简单有用。

阅读更多
用C++和SFML写游戏-纹理及精灵(5.1)

对于 2D3D 游戏来说,纹理是一个很重要的东西,它能够将一张图片映射到我们程序中的对象里。而精灵可以看作是包含其他显示对象的容器。

在这篇文章中,将会讲解一下内容:

  • 读取纹理
  • 将纹理绘制在shape中
  • 什么是精灵
  • 资源管理
阅读更多
用C++和SFML写游戏-事件的处理(4)

处理好来自用户的事件是一个很重要的话题。

首先我们来认识一下什么是事件

一般来说,事件是在某些情况下被触发的对象。它们依赖于操作系统,但是 SFML 为我们提供了一个很好的对象来处理事件,它是独立于操作系统的方式。sf::Event 类,这个类包含了各种事件。

建议阅读 SFML 官方文档去好好了解 SFML 事件的类型,地址是https://www.sfml-dev.org/tutorials/2.5/window-events.php。 注意 sf::Event 是一个联合体(union),使用的时候要恰当的选择我们需要的事件类型。

逻辑上,我们可以将事件分成四种类型:

  • 窗口
  • 键盘
  • 鼠标
  • 手柄
阅读更多
用C++和SFML写游戏-移动我们的Player(3)

在上一节中,我们搭建了游戏的基本框架,用循环处理我们的游戏世界,还掌握了一些方法去解决因机器性能不同而引起的问题。在这一节中,我们将会学习:

  1. 创建 Player
  2. 移动我们的 Player
阅读更多
用C++和SFML写游戏-Game类的创建(2)

这一节我们将会学习到游戏的基本结构,其中的内容包括了:

  • Game类的创建
  • 什么是帧数
  • Player类的创建
  • 事件管理器

Game类

在上一节中,我们用尽可能少的代码创建了一个基本游戏,它包括了:

  • 窗口的创建
  • 图形的绘制
  • 处理用户的输入
  • 将游戏元素绘制到屏幕上
阅读更多
聊聊Android开发中的MVP设计模式

MVP设计模式

Android中的设计模式很多,常用的有 MVC、MVP和MVVM,MVP是目前比较流行的一种设计模式,全称为Model-View-Presenter。从上图可以看到,MVP 设计模式解除了Model和View的耦合。这种设计的优点是有效地降低View的复杂性,避免业务逻辑被塞入View中,使得View变得更为简单专一。不仅如此,MVP 还带来了良好的可拓展性、可测试性,保证系统整洁性、灵活性。对于一个复杂的应用来说,MVP模式是一种良好的架构模式,它可以非常好地组织应用结构,使得应用变得灵活,拥抱变化。

MVP模式下,项目中会多出很多类和接口,这是为了实现Model和View的解耦,View和数据的交互通过接口来实现。虽然多出了这些类和接口,但是这样便于我们维护,使用MVP模式来写项目,可以说是面向接口编程,调用的方法基本都是抽象的,直接使用接口来调用,提高可维护性。在MVP模式中,一般我们都会将要调用的方法抽象成接口,Model、View、Presenter接口,在各实现类中,直接对接口进行操作。

MVP设计模式的好处有以下几点

  1. Activity只处理生命周期的任务,代码变得更加简洁
  2. 视图逻辑和业务逻辑分别抽象到了View和Presenter的接口中去,提高代码的可阅读性
  3. Presenter 可以复用,一个 Presenter 可以用于多个 View,不用去改 Presenter
  4. 利于单元测试。模块分明,那么我们编写单元测试就变得很方便了,而不用特别是特别搭平台,人工模拟用户操作等等耗时耗力的事情。

总之,使用了 MVP 设计模式能让代码逻辑更清晰,改动代码的时候更具目的性,而不是改动一片代码

阅读更多
用C++和SFML写游戏-SFML介绍(1)

我想大多数初学 C++ 的人都有这样一个疑惑,那就是学完这门编程语言之后接下来能做什么。并且大多数初学者学习过程都应该是看着这么一个黑框框。就像是这样

这么一看学习 C++ 确实是挺无聊的一件事,实际上 C++ 能做的事情远比我们想象中的要多,它在游戏、科学计算、网络软件、分布式应用、操作系统、设备驱动程序和嵌入式系统等领域都有应用。应用领域非常广泛,但是缺点也很明显,开发效率低。

这里我会写一个系列文章,通过 SFML(Simple and Fast Multimedia Library)来介绍 C++ 在游戏方面的一些应用,以及如何用它来实现一个简单的游戏,文章内容大多数翻译自《SFML Essentials》(Maxime Barbier《SFML Blueprints》,这本书写的有点复杂,换了一本入门书),有兴趣的朋友可以搜一下这本书。

一、SFML是什么

SFML 是多媒体库,它为PC的各个组件提供简单的界面,用来简化游戏和多媒体应用程序的开发。 主要由五个模块组成,分别是:系统,窗口,图形,音频和网络。

SFML 是跨平台的,通过 SFML,你的应用程序可以在最常见的操作系统上进行编译和运行:Windows,Linux,macOS以及Android和iOS。

SFML 支持多种语言,具体可以在官网查看支持的语言。

二、安装 SFML

安装过程在这里就不详细说了,官网有很详细的过程,根据你的平台选择对应的教程安装就好了。

三、一个简单的小例子

当你把环境都配置好了以后,就可以敲一个简单的例子了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
int main(int argc, char const *argv[])
{
sf::RenderWindow window(sf::VideoMode(400, 400), "Circle");
window.setFramerateLimit(60);

sf::CircleShape circle(150);
circle.setFillColor(sf::Color::Blue);
circle.setPosition(10, 20);

while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed
or (event.type == sf::Event::KeyPressed
and event.key.code == sf::Keyboard::Escape) ) {
window.close();
}
window.clear();
window.draw(circle);
window.display();
}
}
return 0;
}

运行之后的效果如图3.1所示,在一个窗口里面绘制出了一个圆。虽然是一个很简单的功能,但是我们已经脱离了黑框框!通过后面的学习,我们能做的事情会更多。

图3.1

下一篇文章我们将会搭建游戏的基本框架,Game 类的创建。😃


[Offer收割]编程练习赛98

题目1 : 推断上下级

时间限制:10000ms

单点时限:1000ms

内存限制:256MB

描述

H公司包括CEO在内,一共有N名员工,编号1~N,其中CEO的编号是1。除了CEO之外,其他员工都有唯一一名直接上司,形成了一种树形的上下级关系。

现在小Hi知道H公司所有的上下级关系,一共M对。换句话说,只要两名员工A和B之间存在上下级关系(直接或者间接),那么A和B一定在这M对关系之中。

请你帮小Hi推断出每个人的直接上级是谁。

输入

第一行包含两个整数N和M。

以下M行,每行包含两个整数Ai和Bi,代表Ai是Bi的上级。

2 <= N <= 1000

1 <= M <= 499500

输出

输出N行,其中第i行包含一个整数Pi,代表i号员工的上级。(对CEO输出0)

样例输入

3 2 1 2 1 3

样例输出

0 1 1

题解

不能直接套用并查集去解,因为题目要求输出的是每一个员工的直接上级。员工之间的关系是树形关系,不存在回路,使用拓扑排序就可以得到每个员工的直接上级。因为拓扑排序会从这个树形结构的根结点(CEO)开始,当弹出一个节点时,如果相邻节点此时入度为0,则此节点的直接上级就是弹出的节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <iostream>
#include <vector>
#include <queue>
#include <algorithm>
#include <string.h>
#include <stdio.h>
using namespace std;
#define ll long long
const int maxn = 1005;
int in[maxn], fa[maxn];
vector<int> p[maxn];
queue<int> q;
int main(int argc, char const *argv[])
{
int n, m, a, b;
scanf("%d %d", &n, &m);
memset(in, 0, sizeof in);

while (m--) {
scanf("%d %d", &a, &b);
p[a].push_back(b);
in[b]++;
}
q.push(1);
while (!q.empty()) {
int top = q.front();
q.pop();
for (auto it : p[top]) {
in[it]--;
if (in[it] == 0) {
fa[it] = top;
q.push(it);
}
}
}
printf("0\n");
for (int i = 2; i <= n; i++) {
printf("%d\n", fa[i]);
}
return 0;
}


题目2 : 数组划分游戏

时间限制:10000ms

单点时限:1000ms

内存限制:256MB

描述

小Hi在玩一个有关数组划分的游戏。给定一个整数K和一个长度为N的数组A=[A1, A2, … AN],小Hi需要将它划分为K个连续子数组,并对每个子数组求和。

不妨设这K个子数组的和依次是S1, S2, … SK,则小Hi的得分是其中的最小值即min(S1, S2, … SK)。

例如对于A=[1, 2, 3, 4]和K=2,小Hi可以划分成[1, 2]和[3, 4],这样得分是3;也可以划分成[1, 2, 3]和[4],这样得分是4。

对于给定的K和数组A,你能帮助小Hi算出他最多能得多少分吗?

输入

第一行包含两个整数N和K。

第二行包含N个整数A1, A2, … AN。

对于60%的数据,1 <= N <= 1000

对于100%的数据,1 <= K <= N <= 100000 1 <= Ai <= 1000000

输出

一个整数代表答案

样例输入

1
2
4 2
1 2 3 4

样例输出

1
4

题解

二分法解决,二分每块区域的最小值是多少。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <iostream>
#include <algorithm>
#include <string.h>
#include <stdio.h>
using namespace std;
#define ll long long
const int maxn = 100005;
int n, k;
int a[maxn];
ll res = 0;
inline bool ok(int x) {
ll sum = 0, mina = 0x7f7f7f;
int m = 0;
for (int i = 0; i < n; i++) {
sum += a[i];
if (sum >= x) {
mina = min(sum, mina);
sum = 0;
m++;
}
}
if (m >= k) res = max(res, mina);
return m >= k;
}
int main(int argc, char const *argv[])
{
ll sum = 0;
scanf("%d %d", &n, &k);
for (int i = 0; i < n; i++) {
scanf("%d", a+i);
sum += a[i];
}
ll l = 1, r = sum, mid;
while (l <= r) {
mid = (l+r)>>1;
if (ok(mid)) l = mid+1;
else r = mid-1;
}
printf("%lld\n", res);
return 0;
}


阅读更多
理解c语言中的回调函数

来看看维基百科中如何定义回调函数

在计算机程序设计中,回调函数,或简称回调,是指通过函数参数传递到其它代码的,某一块可执行代码的引用。这一设计允许了底层代码调用在高层定义的子程序。

这段话不是那么好理解,不同语言实现回调的方式有些许不同。其实可以这样理解,回调就是 在一个函数中调用另外一个函数

c 语言中,回调是使用函数指针来实现的。 函数指针——顾名思义,是指向一个函数的指针。通常函数指针有两个方面的用途,一个是转换表(jump table),另一个是作为参数传递给一个函数

下面是两个函数指针的声明

1
2
int (*f)(int, float);
int *(*g[])(int, float);

前者把 f 声明为一个函数指针,它所指的函数接受两个参数,分别是一个整型值和浮点型值,并返回一个整型值。后者把 g 声明为一个数组,数组的元素类型是一个函数指针,它所指向的函数接受两个参数,分别是一个整型值和浮点型值,并返回一个整型指针。

需要注意的是,简单声明一个函数指针并不意味着它马上就可以使用。和其他指针一样,对函数指针执行间接访问之前必须把它初始化为指向某个函数。下面的代码段说明了一种初始化函数指针的方法。

1
2
int f(int);
int (*pf)(int) = f;

第 2 个声明创建了函数指针 pf,并把它初始化为指向函数 f。函数指针的初始化也可以通过一条赋值语句来完成。在函数指针的初始化之前具有 f 的原型是很重要的,否则编译器就无法检查 f 的类型是否与 pf 所指向的类型一致。


阅读更多
在Android后台线程中更新UI的几种方法

先来解释一下为什么要在子线程中更新UI。

没有人愿意使用经常卡顿的APP,用户希望他们的App用起来流畅而不卡顿。当然每个开发者也希望这样——没有人会在构建自己的App时会说,“这个App跑的太快了,也许我应该放慢一点速度”;

尽管没有人希望这样做, 那么为什么还会有的App用起来会那么卡呢? 你之前可能已经看过我所说的这些App

可以列出一些导致某些App卡顿的原因,但是我敢打赌排在前10位的原因之一就是主线程上有太多的东西。 它有可能是被I/O处理或者复杂的计算(或者两者兼之)所拖累,这很糟糕。

难道这意味着我们的App中不应该有I/O处理或者复杂的计算吗?显然不是,但是我们应该知道要把这些放在哪里,而不是全部放在主线程中。

阅读更多