2021年3月28日,晚上8点,周日,sfml这边是可以学的!
system module:这些api要配合官网tutorials看。单看哪一个都不好。但一起看就非常好。
sf::Clock
sf::String
sf::Time
sf::Vector2
sf::Vector3
void sf::sleep(Time duration)
std::ostream& sf::err()
window module:
sf::ContexSettings
sf::Event
sf::Keyboard
sf::Mouse
sf::VideoMode
sf::Window
sf:::Style::None=0
sf::Style::Titlebar = 1<< 0
sf::Style::Resize = 1<< 1
sf::Style::Close = 1<< 2
graphics module:
sf::CircleShape
sf::Color
sf::ConvexShape
sf::Drawable
sf::Font
sf::Glyph
sf::Image
sf::Rect
sf::RectangleShape
sf::RenderStates
sf::RenderTarget
sf::RenderTexture
sf::RenderWindow
sf::Shader
sf::Shape
sf::Sprite
sf::Text
sf::Texture
sf::Transform
sf::Transformable
sf::Vertex
sf::VertexArray
sf::View
sf::PrimitiveType
sf::Points
sf::Lines
sf::LineStrip
sf::Triangles
sf::TriangleStrip
sf::TriangleFan
sf::Quads
【3月29日,周一,上午8点】开始sfml graphics部分学习。
核心部分就是在graphics。也是我复习应该起始的地方。
第3章 graphics module
3.1 drawing 2d stuff
不像c++那样 熟练、简洁。是别人的库要学!很大的一个库。
sf::RenderWindow继承自sf::Window和sf::RenderTarget.
sf::RenderWindow win;
win.clear();
win.draw();
win.display();
这3个函数都是来自 sf::RenderTarget里面。【错,clear和draw是,display是window类里的】
游戏循环里面的事件处理的理解:
sf::Event event;
while (win.pollEvent(event)
{
if (event.type == sf::Event::Closed)
win.close();
}
先定义事件,且在游戏循环里面定义? 然后轮询事件!
pollEvent就是轮询事件的意思,会把事件数据写入event对象中。
不是写滑句!
下面再放clear(), draw(), display(). 都放在游戏循环里。
display()是因为draw()有双重缓冲存在。
能draw什么: drawable对象:sprite,text,shape,还有vertex array!
还有vertex。
它们4个有一些共同属性,但每个有细微差别。
除了可以draw到sf::RenderWindow上,还可以draw到sf::RenderTarget的sf::RenderTexture上。
sf::RenderTexture renderTexture;
if (!renderTexture.create(500, 500))
{
std::cout << “error!\n”;
}
renderTexture.clear();
renderTexture.draw(sprite);
renderTexture.display();
const sf::Texture& texture = renderTexture.getTexture();
sf::Sprite sprite(texture);
win.draw(sprite);
如果用OpenGL绘制在renderTexture上,renderTexture.create(500, 500, true);
3.2 sprites and textures
sf::Texture texture;
if (!texture.loadFromFile(“image.png”))
{
// error…
}
working directory在单独运行时是可执行文件的文件夹,在ide启动可执行文件时是项目文件夹。
loadFromImage(“image.png”)
sf::Image
texture.loadFromFile(“image.png”, sf::IntRect(10, 10, 32, 32));
sf::IntRect(10,10,32,32)前面2位是矩形的左上角坐标,后面2位是矩形的尺寸。
texture.create(200, 200); 空的texture。内容尚未定义。稍后在用update()。这是除 texture.loadFromFile(“image.png”)外另一种赋值texture的方法,它的赋值是用像素数据。
sf::Uint8* pixels = new sf::Uint8[width*height*4];//4是因为rgba
…
texture.update(pixels);
第二种,update自sf::Image
sf::Image image;
…
texture.update(image);
第3种,update自当前窗口的内容
sf::RenderWindow window;
…
texture.update(window);
上面3种都假定源像素和texture的尺寸是相同的!如果不同,你可以仅update texture的子矩形。
另外,一个texture有2个属性影响它怎样被render。
第一个: 平滑texture。smooth。texture.setSmooth(true);
第二个:在一个sprite内重复贴图。texture.setRepeated(true);
这用于 sprite的矩形大于texture的情形。否则当然没有效果!
sf::Sprite sprite;
sprite.setTexture(texture);// 只有sprite这里是传入引用,shape和vertexarray都是传指针的!
window.draw(sprite);
sprite.setTextureRect(sf::IntRect(10, 10, 32, 32));
sprite.setColor(sf::Color(255,255,255, 128));//半透明
变换:位置、朝向、缩放
sprite.setPosition(sf::Vector2f(10.f, 50.f)); //绝对位置
sprite.move(sf::Vector2f(5.f, 10.f)); // 相对于当前位置移动的位置
sprite.setRotation(90.f);// 绝对角度
sprite.rotate(15.f);//相对旋转角度
sprite.setScale(sf::Vector2f(0.5f, 2.f));
sprite.scale(sf::Vector2f(1.5f,3.f));
只有spite可以变换,texture是不能变换的,texture也是不能draw到RenderWindow上的。这就是组织! 因为必须是 transformable和drawable的对象才能变换和绘制。
默认的原点是 sprite的左上角!
sprite.setOrigin(sf::Vector2f(25.f, 25.f));//相对于左上角移动,而不是设置绝对坐标。
白矩形问题:
你成功加载了texture,正确构造了sprite,但屏幕上显示了一个白色方形。这是怎么回事?
sf::Sprite loadSprite(std::string filename)
{
sf::Texture texture;
texture.laodFromFile(filename);
return sf::Sprite(texture);
}// 错误,texture在这里销毁了
使用尽可能少的texture的重要性:是一个好的策略。原因简单:改变当前的texture贴图对显卡是一个昂贵的开销。绘制使用同一个texture的sprite产生最大的性能。
3.3 text and fonts
【下午3点15】sfml好后还要应用cpp呢。仍然是个大目标!所以带上能量学习 sfml!
如果能量好,就能进入好的学!
【下午3点45】还是不太能学!
sfml不好学吗,它只是需要学会用的库。不是c++的那么难。 它只需要了解,只需要知道结构!相比cpp,cpp需要知道的为什么比这个要多,要深入。 还有Qt,MFC,它们都只是库。只是多!只是多!最厉害的是cpp!
sf::Font font;
font.loadFromFile(“arial.ttf”);
字体文件在windows上可以在windows文件夹里找一个。对mac,怎样获得字体文件呢?system/library/fonts
上一节 texture和sprite是加载图片进来,这一节是字体和文字的绘制。
但是学完sfml距离cpp好远!要很长时间才能看完sfml的书,而这个时间长度会让人把cpp忘光!
怎么解决这个问题呢? sfml尽量快速推进它的学习进程?尽快到用上。先不需要到sfml的高级用法!
sfml如果学好再放下学cpp则又会忘记sfml。sfml尽量带着能量快速学成!
sf::Text text;
text.setFont(font);
text.setString(“Hello World”);
text.setCharacterSize(24);
text.setFillColor(sf::Color::Red);
text.setStyle(sf::Text::Bold|sf::Text::Underlined);
win.draw(text);
Text可以被变换: 它有坐标位置、方向和缩放。相关的函数和sf::Sprite类的是一样的。
怎样避免非ASCII字符的问题?
前面加L。
text.setString(L”章”);
加L就可以,它会处理遇到 gbk或utf-8编码的代码会怎样编码解码。
sfml还不支持 c++11的utf-8,utf-16,utf-32字符串字面值。
字体文件还要支持所选的字符。比如英文字体文件就不能显示出 中文字符来!
新建你自己的text类
超范围,用不到!
3.4 Shapes
sfml里和pygame里一样都有surface,就是sfml里的RenderTarget,窗口和图片texture都可以是surface, 但shape不行,shape不是renderTarget。它是drawable和transfomable的,可以向renderTarget上draw。这就跟pygame是统一的了,但是sfml里的矩形跟pygame里的外接矩形不一样!sfml里的矩形是什么样的;pygame里的外接矩形是什么样的,反正都有,但实现方式不一样。sdl和sfml是差不多的库。
共通形状属性
1,变换(位置,旋转,缩放)
2,颜色
sf::CircleShape shape(50.f);
shape.setFillColor(sf::Color(100, 250, 50));
3,边缘
sf::CircleShape shape(50.f);
shape.setFillColor(sf::Color(150, 50, 250));
shape.setOutlineThickness(10.f);
shape.setOutlineColor(sf::Color(250, 150, 100));
默认边缘线宽是向外增长的。设置为负值可以向内增长。
shape.setOutlineThickness(0);无边缘宽度。如果你只想要边缘线,你可以设置形状的填充颜色为sf::Color::Transparent。不是设置边缘的填充颜色为透明,而是设置形状的。
4,贴图Texture
形状可以附加贴图,就像sprite一样。
setTextureRect函数,指定texture的一部分映射到shape上。
它把 texture矩形映射到 形状的外接矩形上。这个方法不提供最大的灵活性,但它更容易使用。【想看古希腊的文学!】它比单独设置shape的每个顶点texture坐标要方便。
sf::CircleShape shape(50);
shape.setTexture(&texture);
shape.setTextureRect(sf::IntRect(10, 10, 100, 100));
注意:边缘不被贴图。贴图和形状的填充颜色是叠加的。如果形状本身的填充颜色是白色sf::Color::White,贴图就是原样。
要取消 texturing,调用setTexture(NULL);
绘制一个形状
和绘制其它的sfml实体是一样的简单。
win.draw(shape);
内置shape类型
矩形:
sf::RectangleShape rectangle(sf::Vector2f(120.f, 50.f));
rectangle.setSize(sf::Vector2f(100.f, 100.f));
没有位置,只需要尺寸就可以!
下面做什么呢? 只在走量,再多可能不适合记忆规律。
下面做什么?
元素还没学全 !sfml
学点cpp?【下午4点50分】
【晚上7点20】sfml❤️笔记!
从3.2 开始是主场,
texture和sprite,材质(或叫贴图)与 精灵(历史原因起的这个名,原始含义至今仍不清楚,是transformable和drawable的2个类方法的纯集合体,没有其它的任何额外的内容。sprite),名字翻译准确!
验证精灵上面的说法对不对!magic!
图的渲染是一回事,移动、旋转、缩放是 另一回事,所以都是分开的!还有shader的变形,那也属于变换,只是效果跟基础的3种相比更加特殊!
其实transform,shader包括texture都是renderStates,还有一个blendMode。
texture的定义里,全部方法都有什么?通过查这可以完全认识texture!
loadFromFile(),反正这些loadFrom的函数,都有一个第二可选参数,只选择原始图片的一部分(矩形的)! 除了这个地方能选一个子矩形——还有在texture往sprite里放的时候还能选一个子矩形。这些都是简单的设计。但这里面也有平凡的东西,如那些基础概念。总之,它确实在设计上有很多简单在里面!这对 库 来说非常重要!
大纲、清晰的教材!
sf::Texture texture; //只是一种构造函数还是只有这一种?
上面当然Texture还有:它的定义里有额外的一个身份:RenderTarget。
只有一个默认构造函数,还有当然的复制构造函数,没有其它构造函数了!
它的方法:create()它跟窗口的create()方法不一样,完全不一样,虽然它们都是rendertarget,texture这里的create是就它是texture(图片数据)而存在的一个方法,而不是关于它是RenderTarget而存在的create。Window或RenderWindow的create也不是就它是rendertarget而存在的create,而是就它是一个opengl的上下文环境窗口!
另外RenderTarget是一个类吗?sf::Texture没有继承自 它呀!而只继承自sf::GlResource.
【今天晚上的感觉,sfml看到了它的简单风格或说哲学指导!直感到真选对了 sfml!】
Rendering to a texture can be useful in a variety of situations:
precomputing a complex static texture (like a level's background from multiple tiles)
applying post-effects to the whole scene with shaders
creating a sprite from a 3D object rendered with OpenGL
etc.
RenderTexture不是Texture。
用mac mini做笔记就怕停电!
【晚上8点25,Win10电脑上】今天晚上的学习状态太好了!能学sfml,那就抓住机会。而且都不惧没开护眼模式。长期来说还是开吧!
28寸4k电脑才觉得正好。以前真是委屈了。
仍然,援引 官网tutorial做sfml的笔记。
texture的加载方法:最常用的是loadFromFile("image.png")
还有loadFromMemory(),loadFromStream(),loadFromImage(), 总共4种。
sf::Texture的所有方法:
1,构造函数只有默认构造函数,不用看了。复制构造函数和析构函数不用看了。
2,下面就是加载方法,4个loadFrom...方法。4个加载方法都有可选第二参数 const IntRect& area=IntRect()
3,create(unsigned int width, unsigned int height)创建空像素的矩形!
4,getSize() const返回该texture的尺寸。
5,copyToImage() const 返回类型是sf::Image
6,update() 很多种对象,可以是像素数组,其它texture,image,window
7,setSmooth(bool smooth)
8, isSmooth() const
9, setSrgb(bool sRgb)还有isSrgb()const
10,setRepeated(bool repeated)还有isRepeated()const
11,
友元:Text类,RenderTexture类,RenderTarget类。
所以,Texture就是图片数据,加载进来(到程序中)。
它没有图片的处理,因为它在显存中,不在内存中!
sf::Image类,Image也是图片的数据,但它在内存中,它要绘制的话 只能 通过先到Texture中再到Sprite中这样的路线!当然Texture也可以到Shape中(除了到Sprite中)!但不能到文字Text中!
Image类有处理图片,但(不知道 从内存往显存转还是从显存往内存转)操作是开销很大的。
下面看一下sf::Image有哪些方法:
1,构造函数只有一个默认构造函数。
2,create()有2个重载方法,创建一个image,并且用一个专有的颜色填充它。另一个重载是从一个像素数组创建图片。
3,loadfromFile()memory,stream共3个加载方法。从texture到image用copytoimage,从image到texture用load from image。
4,bool saveToFile(const std::string& filename) const
5, Vector2u getSize() const 返回image的尺寸。
6,void createMaskFromColor(const Color& color, Uint8 alpha=0) 创建一个透明 屏蔽
7,void copy(const Image& source, unsigned int destX,unsigned int destY, const IntRect& sourceRect = IntRect(0,0,0,0), bool applyAlpha=false) 从另一个图片向这个图片复制像素。
8,setPixel() 改变一个像素的颜色
9,getPixel()获取一个像素的颜色
10,getPixelPtr()获取一个指向一个像素数组的只读指针。
11,flipHorizontally()左右对换图片
12,flipVertically()上下对换图片
总结: Image能改图片像素数据、上下、左右转换,去除背景色,叠加图片,导出图片到文件。它不需要裁剪图片。
下面回到texture:
前面讲了加载:和其它方法,texture的本质就是显存里的图片数据。它就是有 加载方法。还有setSmooth,但是setRepeated像是应该在Sprite里面。但Sprite不管这些!
下面还要查到重要的sprite!
loadFrom的第二参数:具体:
texture.loadFromFile("image.png", sf::IntRect(10, 10, 32, 32));
IntRect类是一个表示矩形的工具类。单纯地表示矩形!
如果不想加载图片到texture里来,可以用update()加载其它东西!
所以从这里看,更宽广的texture的定义是:它是像素数据的载体(显存中),不一定是 图片!
学到这,sfml的核心----图片多么简单!这是哲学风格!
这太好了!
这就能很快地进入用sfml+cpp了!!!
sf::Texture texture;
texture.create(200, 20);
sf::Uint8* pixels = new sf::Uint8[width*height*4];
...
texture.update(pixels);
sf::Image image;
...
texture.update(image);
sf::RenderWindow window;
...
texture.update(window);
除了这3种,还有texture自身, 它可以从另一个texture更新到它里面。
update(window)还有image,还有 texture有什么用呢?
到sprite了!
先看sprite是什么!
语法看上去像是就是drawable的东西!
sf::Sprite sprite;
sprite.setTexture(texture);
window.draw(sprite);
设置只收进来texture的部分矩形:
sprite.setTexture(sf::IntRect(10,10, 32, 32));
到这里,下面看看Sprite有哪些方法,公共的!:
【3月30号,周二,上午6点半】sfml可以说达到废寝忘食的地步!早上起来就想学,入门教材也是世上最好,没有之一!入门之后还要用还要参考那4本英文教材。这个键盘也是绝对最好!
看完sprite要上 3.3 text and font还是返回3.1 看一下rendertarget中的RenderTexture?
看一下sprite是什么?基础的它是drawable的对象,而texture只是导入到程序进来的图片像素数据。第二它是transformable的对象。看sprite能否也能建立空白像素数据?texture能(用create方法),sprite应该没有这种共,sprite就像一个负责封装的空壳子,经过它之后才能绘制到绘制目标上、才能变换!下面看实际:
sf::Sprite就是继承自sf::Drawable和sf::Transformable这2个基类!可能自己再添加一些东西!
1,构造函数之一,默认构造函数
2,带一个参数的构造函数Sprite(const Texture& texture)
3,带2个参数的构造函数 Sprite(const Texture& texture, const IntRect& rectangle) 只取texture的子矩形。
texture从图片导入到程序来是就能选图片的子矩形,texture还可以不导入图片,而创建自己的像素数据。
4,void setTexture(const Texture& texture, bool resetRect = false)配合默认构造函数的方法。
5,void setTextureRect(const IntRect& rectangle)选择sprite要显示的texture的子矩形,配合2参构造函数。
6,void setColor(const Color& color)设置sprite的全局颜色。这个颜色和texture是叠加效果。
7,const Texture* getTexture() const 获取sprite的源texture。
8,const IntRect& getTextureRect() const 获取sprite显示的texture的子矩形。看来texture是全部加载到sprite里来的,但显示哪些可以再选定一个矩形。【先基本概念,再简单方法。】sprite的概念就是负责封装来显示、变换的空壳,然后这里除了有加载进来、设置颜色,还有获取源贴图和获取通过sprite显示的texture的矩形区域。
9,const Color& getColor() const获取的是:sprite的全局color。要获取texture的局部像素color要用image。
10,FloatRect getLocalBounds() const 到非常重要的 方法了!获取外接矩形。在pygame里是可以通过外接矩形移动 图片的,但在sfml里不是这样的,sfml里外接矩形仅用于判断边界是否碰撞到了,不是通过外接矩形移动sprite的,sprite的移动是通过从sf::Transformable继承来的setPosition()和move()方法。getLocalBounds是获取实体的本地的外接矩形,变换后的不包括的!
11,FloatRect getGlobalBounds() const 获取实体的全局的外接矩形。包括变换后的!获取外接矩形不是继承自transformable或drawable的,而是sprite自己定义的。但是如shape,text里面应该也都有,每次都自己定义吗?查一下!答案:它确实不是继承的,而是sprite、text、shape里自己都定义一遍!
12,void setPosition(float x,float y)设置对象的位置。这些就是都是继承自transformable的了!
13,void setPosition(sf::Vector2f& position)同上,只是坐标的提供方式不一样,推荐这个!
14, void setRotation(float angle)设置绝对角度,参考原点是左上角,正方向是。。。是顺时针。相关联的是 rotate和getRotation方法。
15,void setScale(float factorX,float factorY)设置对象的缩放因子。
void setScale(sf::Vector2f& factors)
16,void setOrigin(float x, float y) 首先这个也是继承自transformable的,它不考虑变换,是相对于左上角。
void setOrigin(sf::Vecor2f& origin)
17,const Vector2f& getPostion() const 获取对象的位置。
18,float getRotation() const
19, const Vector2f& getScale() const
20, const Vector2f& getOrigin() const
21, void move(float offsetX, float offsetY)
void move(const Vector2f& offset)
22, void rotate(float angle)
23, viod scale(float factorX, float factorY)
void scale(const Vector2f& factor)
24, const Transform& getTransform() const
获取对象组合的变换!
25, const Transform& getInverseTransform() const
获取对象的Inverse相反的组合变换。需要上机试它们2个 到底返回什么?
sprite是texture的drawable表示,有自己的变换、颜色等!
能够容易地显示一个或部分的texture到绘制目标上。
如获取外接矩形等是 sprite自己添加的(在继承基础上!)。
The separation两个分离 of sf::Sprite and sf::Texture allows more flexibility and better performances更灵活和更优化的性能: indeed a sf::Texture is a heavy resource是重资源, and any operation on it is slow (often too slow for real-time applications对实时应用来说太慢了). On the other side, a sf::Sprite is a lightweight object是轻对象 which can use the pixel data of a sf::Texture使用texture的像素数据用它自己的变换、颜色、混合属性来绘制 and draw it with its own transformation/color/blending attributes.
It is important to note that the sf::Sprite instance doesn't copy并不复制贴图 the texture that it uses, it only keeps a reference to it只保留贴图的引用. Thus, a sf::Texture must not be destroyed while it is used by a sf::Sprite (i.e. never write a function that uses a local sf::Texture instance for creating a sprite).
See also the note on coordinates坐标 and undistorted无失真的 rendering in sf::Transformable.
A note on coordinates and undistorted rendering:
By default, SFML (or more exactly, OpenGL) may interpolate插值 drawable objects such as sprites or texts when rendering. While this allows transitions like slow movements or rotations to appear smoothly, it can lead to unwanted results in some cases, for example blurred模糊 or distorted失真 objects. In order to render a sf::Drawable object pixel-perfectly, make sure the involved coordinates allow a 1:1 mapping of pixels in the window to texels (pixels in the texture). More specifically, this means:
The object's position, origin and scale have no fractional part没有小数部分
The object's and the view's rotation are a multiple of 90 degrees是90度的倍数
The view's center and size have no fractional part
下面回到sprite!
sprite已经完备!
3.3 节 text和font
【上午8点】怎样学呢,要不要上机实践一会儿?
text和font的关系?:font是重资源,text是轻对象!关系跟sprite和texture的关系类似。
1,加载font,就像texture介绍完它是什么(图片像素数据)就开始讲加载。这里也是。
其它程序要打印文字也是这样,要先有字体文件。
sf::Font有3个主要特征: 加载一个字体,从它里面获取glyph(比如可视字符),读取它的属性。
典型的程序中只需要用到 第一个特征(就是加载)。
sf::Font font;
font.loadFromFile("arial.ttf");
它是返回真假的,打开成功返回真。
应该也是只有这一个构造函数,就是无参数的默认构造函数。跟texture一样。
还有loadFromMemory和loadFromStream。
下面就开始学text了!我们跟着学,因为sf::Font应该跟texture一样,最重要的是加载。个性先不用了解!以后用到慢慢再说。这是顺序!
sf::Text也是继承自sf::Drawable和sf::Transformable的,再加入它自己的特色!
sf::Text text; //这只是其中一种构造函数,就像sprite一样,还可以构造时就加进 “像素数据”texture,font。
text.setFont(font);
text.setString("Hello World");
text.setCharacterSize(24);
text.setFillColor(sf::Color::Red);
text.setStyle(sf::Text::Bold | sf::Text::Underlined);
...
win.draw(text);
所以,text的特有方法还是很多。而sprite的特有方法跟它不同。text也有getGlobalBounds()方法。
对于非ASCII字符,前面加L。
创建自己的Text类: 首先,有必要吗?内置的text类是功能很齐全的。也难理解一些。sf::Glyph glyph= font.getGliph(character, characterSize, bold);
目前用不到,用到时再研究!
3.4 shape形状
形状就没有font,texture需要加载了。所以它们有区别,但都遵循简单风格哲学!
它是有技术含量的,不是谁都能学成的。没有人指导你你就不好学会。所有的编程里的东西都要有简单哲学。这才是正确道路!网络编程也不会难。
每种形状都有单独的类,但它们都继承自一个共同基类,它们有共性。每个形状类又新增它自己的专有成员:radius属性对于circle类,尺寸对于rectangle类,point对于多边形polygon类。
shape共性:
1变换transformation(position,rotation,scale)
2color颜色setFillColor(sf::Color& color); 这些看sf::Shape类就会有的。
3outline边缘线setOutlineThickness(float thickness); setOutlineColor(sf::Color::Red);
默认边缘线宽度向外增长,如果要向内增长,用负数;设置为0没有边缘线;只要边缘线可以设置填充颜色为setFillColor(sf::Color::Transparent);
4Texture 形状可以贴图化,就像sprite一样。
要指定texture的一部分(子矩形)映射到shape,必须使用setTextureRect函数;在sprite里实现的话用第二参数就能实现,这里却是一定要用单独的一个setTextureRect函数。当然sprite也有一个setTextureRect函数,跟这里的用法一样。但sprite还有第二参数设置子矩形的方法,而shape里没有,只能单独再用一个函数!是这样的。看来这里有需要注意的注意点!一个是这里的setTexture没有第二参数的重载,一个是下面 的传地址,不是传引用!
sf::CircleShape shape(50);
shape.setTexture(&texture); //怎么成了传地址了,不是传引用吗?查看一下api:确实区别于sprite,sprite是传引用, shape的setTexture是传指针地址!!
shape.setTextureRect(sf::IntRect(10,10,100,100));
边缘线是不被 textured。
texture和形状的填充颜色是叠加效果。这一点跟sprite的全局颜色与贴图叠加效果是一样的。
要取消材质,setTexture(NULL).或者用nullptr也可以。
下面绘制形状:
win.draw(shape); //这里draw方法有第二参数的情况?到vertex array再说。
内置形状类型:
上面的总结了,我就不用到 api再抄录一遍详细的shape的api了。大体上就这些。以后用到再查询!
1,Rectangle
查一下api到这里:
RectangleShape(const Vector2f& size=Vector2f(0,0));//带默认参数的构造函数。
void setSize(const Vector2f& size)
const Vector2f& getSize() const
还有2个不常用的:
virtual std::size_t getPointCount() const 看返回类型就不需要了解这个方法了。
virtual Vector2f getPoint(std::size_t index) const 看形参就不用了解 了。
其它的都是上面讲过的了!
所以sf::RectangleShape就是构造函数和setSize()这两个需要知道。这是全部的。
2,Circle
sf::CircleShape 有2个属性,radius和变数。因为计算机里没有完美的圆,是正多边形。默认边数是30.
sf::CircleShape circle(200.f);
circle.setRadius(40.f);
circle.setPointCount(100);
就只有一个带2个默认值参数的构造函数。
CircleShape(float radius = 0, std::size_t pointCount=30)
void setRadius(float radius)
float getRadius() const
void setPointCount(std:;size_t count)
下面也有
virtual std::size_t getPointCount() const 获取制造这个圆的点数
virtual Vector2f getPoint(std::size_t index) const 仅获取一个点(一个组成该形状的顶点)
剩下的都一样了。
形状 也和sprite一样,有getTransform()方法和getInverseTransform()方法。
3,规则的多边形
没有专有的类来绘制规则多边形(就是指正多边形)。
用sf::CircleShape triangle(80.f, 3);就是等边3角形。
sf::CircleShape squre(80.f, 4);就是正方形。当然也可以RectangleShape画出来。
sf::CircleShape octagon(80.f, 8);正八边形。
4,凸形Convex shape
这是终极的 形状类了!
sfml是不能直接绘制concave shape凹形的。要绘制凹形要先把它切分为多个凸形。
下面是语法:
sf::ConvexShape convex;
convex.setPointCount(5);
convex.setPoint(0, sf::Vector2f(0.f, 0.f));
convex.setPoint(1, sf::Vecor2f(150.f,10.f));
...
convex.setPoint(4, sf::Vector2f(0.f, 50.f));
定义顶点的顺序很重要。必须全部定义,或者以顺时针clockwise或者以逆时针counter-clockwise顺序。
它有一个带默认值参数的构造函数ConvexShape(std::size_t pointCount=0)
viod setPointCount(std::size_t count)
virtual std::size_t getPointCount() const
void setPoint(std::size_t index, const Vector2f& point)
virtual Vector2f getPoint(std::size_t index) const
剩下的就都一样的了! 上面的其它图形只有getPointCount和getPoint方法,不会有setPiont方法。
convex shape内部是使用 triangle fans来构造的!三角形扇,还有triangle strip三角形条带。
5,直线line
第一种情况直线有宽度:
sf::RectangleShape line(sf::Vector2f(150.f, 5.f));
line.rotate(45.f);
第二种情况:直线没宽度
sf::Vertex line[]
{
sf::Vertex(sf::Vector2f(10.f,10.f)),
sf::Vertex(sf::Vector2f(150.f, 150.f))
};
win.draw(line, 2, sf::Lines);
需要学vertex array后!
最后,自定义形状类型。这个就像自定义text一样,也用不到。内置的已经很足够!
椭圆形确实需要。但先略过!用到cos(angle),sin(angle)这些数学函数。
抗锯齿图形
不能单独设置图形抗锯齿,而是全局的,在创建窗口的时候。
sf::ContexSettings settings;
settings.anlialiasingLevel = 8;
sf::RenderWindow window(sf::VideoMode(800, 600),"sfml shape",sf::Style::Default, settings);
抗锯齿依靠显卡支持。
3.5 用顶点数组设计你自己的实体
开始不那么简单了?
有点儿底层,需要记的东西多一些?
第一,它是什么,它是绘制图形的另一种方式吗?那根本用不到,因为用shape类就足够用了。不需要这种第二选择!所以第一搞明白它是什么。
第二,后面还有view,shader倒是不太重要。
前面的的shape并不是最有效率的解决方式。比如,如果你绘制大量的sprites你会很快达到显卡的性能限制。
原因是性能很大程度取决于 调用draw函数的次数。
实际上每一次调用都包含 :设置一系列opengl状态、重置矩阵、交换贴图等等。
即使仅仅绘制2个三角形(一个sprite)也需要调用这些。这里的sprite是广义的sprite,它包括图片,形状,字体。
这远远没有达到显卡优化:当今的gpu被设计得对大批量的三角形的处理,通常是几千到百万。
要填补这个差距,sfml提供了低层次机制来绘制东西:顶点数组。
实际上,顶点数组已经被其它sfml类内部使用了。
它们允许更灵活地定义2d实体,包含你需要的大量的三角形。它们甚至还允许绘制点和线!
2,什么是顶点,为什么它们总在数组中?
【学完这一节后看看vertex array到底是不是就是另一种绘制shape的方法?如果是的话就根本不用用它,了解即可。因为sfml的shape背后就是用顶点数组优化的。】
顶点是你可以操纵的最小的图形实体。简单说,它是一个图形点: 自然地,它有一个2d位置 (x,y),但它还有一个颜色,还有一对 texture坐标。
顶点们(vertex的复数)自己做不了什么。
它们是被组织到 图元primitive中起作用的:
Point(1个顶点),line(2个顶点),triangle(3个顶点), 或者quad(4个顶点)四边形。
然后你就可以组合不同的多个primitives来创建最终的几何实体。
几何实体,这是重点。它就是绘制 shape的!
顶点数组是讨论对象,因为它就是primitive的表示,单独一个vertex是没什么用处的。
3,一个简单的顶点数组
看实现了:
sf::Vertex 类, 它只有一个构造函数,没有任何其它方法成员了。属性成员都是公有的。
sf::Vertex vertex;
vertex.position = sf::Vector2f(10.f, 50.f);
vertex.color = sf::Color::Red;
vertex.texCoords = sf::Vector2f(100.f,100.f);
它vertex的属性成员是公有的。
sf::Vertex vertex(sf::Vector2f(10.f,50.f),sf::Color::Red, sf::Vector2f(100.f, 100.f));
现在让我们来定义一个primitive。
一个primitive由多个 vertex组成,把这些vertex放到数组里,sfml里不是放到普通数组,而是放到sf::VertexArray里。sf::VertecArray类似于std::vector。它的元素类型、数组类型是有定义的。
sf::VertexArray triangle(sf::Triangles,3); //比vector要复杂
triangle[0].position = sf::Vector2f(10.f, 10.f);
triangle[1].position = sf::Vector2f(100.f, 10.f);
triangle[2].position = sf::Vector2f()100.f, 100.f);
triangle[0].color = sf::Color::Red;
triangle[1].color = sf::Color::Blue;
triangle[2].color = sf::Color::Green;
// 这里没有texture坐标,我们后面再看texture坐标。
window.draw(triangle);
颜色是插值地填充在primitive里的。这里的primitive就是一个三角形。就是一个 普通三角形,不是什么primitive。它可能是指组成单元的最小单元。
不是非要使用上 sf::VertexArray类。它定义是为了方便的。它不过是 一个std::vector
所以还是老实用 sf::VertecArray 类吧!
std::vector
vertices.push_back(sf::Vertex(...));
...
window.draw(&vertices[0], vertices.size(), sf::Triangles);// 差不多看懂sf::VertexArray了。
这里面第一个参数表示取vertices的地址!这种写法不如直接写 vertices!错误!这里的参数必须这种写法。
sf::Vertex vertices[2] =
{
sf::Vertex(...),
sf::Vertex(...)
};
window.draw(vertices,2,sf::Lines);
这是绘制一条直线!(线段)
所以到此,知道primitive了 !它就是组成图形的基本单元。圆是用多边形做出的啦!
4,Primitive类型
我们能创建几种类型的primitive呢?:只能是内置的4种:
sf::Points
sf::Lines 总是一个像素宽。忽略当前的变换和view。
sf::Triangles 不相连的三角形
sf::Quads
最后一种quad实际上仅仅是为了方便而存在的,内部显卡是把它分解成2个三角形的!
sf::LineStrip 一系类相连的线,一条线的末端是另一条线的起点。
sf::TriangleStrip 一系列相连的三角形,每个三角形共享它的2个末端顶点给下一个三角形。
sf::TriangleFans 扇形的。连接到一个中心点。
不详细看了。总之就是 组合图形的基本单元。要拆分图形,比较麻烦。还是说:用内置的shape就够用了!
5,下面还有Texturing顶点数组。
顶点数组sf::VertexArray就是用primitives组合你想要的图形!真的很麻烦,偏底层。应该用别人已经写好的组合图形,就是sfml里 的shape形状们!
sf::VertexArray vertices;
window.draw(vertices, &texture);
这里,因为顶点数组是底层的,他里面没有 存texture信息的地方,不想sprite的setTexture和shape的setTexture,它可不会封装这些。但sfml在draw里提供了接口,可以让你在这里传 进入。仅限于vertex array这样做。高级的类sprite和shape用不到!
6,还有 transforming 一个顶点数组。
还有sf::Transform和sf::RenderState, 要看sf::Transform类。
顶点数组是底层的类,它也不会存储transform的信息,要在 窗口的draw里写transform信息。具体语法了解一下哦:教程里没写,不去查api了解了!
7,创建一个像sfml的实体
这一节内容还挺多。看来对理解sfml很重要!必须看了!【11点。休息10分钟】
根本是:sf::VertexArray 是底层的类。它没有继承transformable,它只继承了drawable。是这样。
它也没有其它像setTexture这样的定义。那这样它怎么实现 texturing和transform呢?
sf::Drawable, sf::Transformable这两个类是sfml的基本类。必须要认识它们2个 。是sprite,text和shape的基础。
sf::Drawable: 是一个接口:它仅声明 了一个 纯虚函数,没有数据成员也没有实体函数。继承自sf::Drawable可以实现自定义类像sfml类一样可以绘制实体。
class MyEntity : public: sf::Drawable
{
private:
virtual void draw(sf::RenderTarget& target, sf::RenderState states) const;
}
MyEntity entity;
window.draw(entity);
还有一个类RenderTarget
sf::Transform和sf::RenderState,sf::Drawable, sf::Transformable ,总共这5个类,要研究sfml的基本了!
【上午11点25】
在vertex array里draw texture就会超级快吗?
看来还是必须学的!vertex array和 上面这5个基本类!
【下午2点25】中午没睡着,躺了至少有一个小时,现在精神也可以。
1,RenderTarget类
1, void clear(const Color& color= Color(0,0,0,255)) 用黑色清空画布
2, void draw(const Drawable& drawable, const RenderStates& states=RenderStates::Default)
3, void draw(const Vertex* vertices, std::size_t vertexCount, PrimitiveType type, const RenderStates& states=RenderStates::Default)
4, void draw(const VertexBuffer& vertexBuffer, const RenderStates& states=RenderStates::Default)
5, viod draw(const VertexBuffer& vertexBuffer, std::size_t firstVertex, std::size_t vertexCount, const RenderStates& states=RenderStates::Default)
vertex array是drawable对象。
一共4个draw方法。
前2个在tutorials中讲到了。
6, virtual Vector2u getSize() const=0
7, virtual bool setActive(bool active=true)
它的构造函数不是public成员,而是protected成员。因为就是window,texture可以作为render target,其它的东西不能自己设定为render target。
它使 绘制2D实体(比如sprite,shape,text)不需要使用直接的opengl命令 成为可能!
sf::RenderTarget还能使用sf::View,是一种2D摄像机。通过views你可以全局地滚动、旋转或者缩放被绘制的所有东西,而不需要变换任意一种实体。
在它之上,render targets仍然能够绘制直接的opengl事情。甚至能混合 调用opengl函数调用和常规的sfml绘制命令。当这样做的时候,确保opengl states是没有通过调用pushGLStates、popGLStates函数弄混乱的。
display()函数没在RenderTarget类中,而在RenderWindow和RenderTexture中有。
下面看sf::RenderStates和sf::Drawable
1,sf::RenderStates 绘制状态
可以应用到要绘制对象上的 有4个全局状态
1,混合模式 对象的像素怎样与背景混合
2,变换 对象是怎样 放置位置、旋转和缩放的
3,材质 什么图片被映射到了对象上
4,着色器 什么自定义效果被应用到了对象上
高级的对象,比如sprites,text在被绘制时 强制这些状态。比如,一个sprite会设置自己的材质,所以绘制sprite时你不需要关心这一点。
变换 是一个特殊情况: sprite,text,和shape都组合了它们的变换和发送到RenderStates结构体中的变换。所以你可以在 每个对象的变换上面 使用“ 全局的”变换。
大多数对象,尤其是高级drawable对象,不需要显式地定义render states而直接绘制----在大多数情况下默认的states设置是可以的。
window.draw(sprite);
如果你想使用一个单独的专有的 绘制状态render state,比如一个shader,你可以直接传送到draw函数里:sf::RenderStates对每一个状态都有一个隐式的一个参数的构造函数。
window.draw(sprite, shader);
当你在一个drawable对象的draw函数内时,你可以 或者传入未更改的render states,也可以改变它们中的某些。比如,一个transformable对象会组合当前的变换到它自己的变换(意思就是不需要取改动render states)。一个sprite会设置自己的texture。等等。
1,RenderStates()默认构造函数,没有参数。
2,RenderStates(const BlendMode& blendMode)
3, RenderStates(const Transform& theTransform)
4, RenderStates(const Texture* theTexture)
5, RenderStates(const Shader* theShader)
6,最后一个构造函数是组合这4项的。
上面6个都是公有构造函数,下面4个都是公有数据成员。
就是 blendmode, transform, texture,shader。
还有一个静态公有数据成员 static const RenderStates Default
Default的定义竟然看不到,静态数据成员的初始化如果是private的就只能在头文件里初始化,它是public的。
总结:绘制状态,render states,在render target中接收这样的参数。
下面看sf::Drawable,
1,公有函数只有析构函数(虚析构函数)。它是个基类。
2,保护成员函数有一个纯虚函数
virtual void draw(RendrTarget& target, RenderStates states) const =0
要在下面的的继承类中定义的。没有定义的话下面的继承类也是抽象基类,就不能定义对象了。
但是如sprite中定义了draw函数了吗?答案:没有!那是怎么回事?应该是在编译后的目标文件中了。是定义了的,在私有中定义 了!
3,友元类,RenderTarget,
4,但它没有数据成员,也没有构造函数,看来使用默认的隐含的构造函数。
问题:drawable对象为什么就可以在RenderTarget上绘制呢?到底是怎么回事?能牵连到什么?
认识drawable!
virtual void draw(RenderTarget& target, RenderStates states) const = 0;
it allows this nice syntax "window.draw(object)" rather
than "object.draw(window)", which is more consistent一致的 with other
SFML classes.
看一下为什么把RenderTarget类定义为友元了,即RenderTarget类是怎样使用sf::Drawable类的纯虚函数的?
void RenderTarget::draw(const Drawable& drawable, const RenderStates& states)
{
drawable.draw(*this, states);
}
所以Drawable对象才能在RenderTarget上绘制,调用方式就是这样的,源代码。最终是用的vertices的draw重载。
下面看sf::Transform 和sf::Transformable,
下面就是transform和renderStates的关系了!前面drawable和renderTarget的关系已经解决!
decompose分解
先看Transform,
定义了一个3*3的变换矩阵。
一个sf::Transform指定 如何来 变换,旋转,缩放,剪切,投影,任何事情。
在数学术语中,它定义怎样变换一个坐标系统到另一个。
比如,如果你对一个sprite应用一个旋转变换,结果就会是一个旋转了的sprite。并且任何通过这个旋转变换而变换 了的事情都同样地 旋转了,相对于它的初始位置来说。
变换典型地是用于 绘制drawing。但它们也可以被用于任何计算,就是那些需要转换local和global坐标系的点的实体(比如碰撞检测)。
1,构造函数,无参数的默认的
2,3*3矩阵的构造函数,Transform(float a00, float a01, float a02, float a10, float a11, ..., float a22)
3, const float* getMatrix() const
4, Transform getInverse() const 返回变换的相反。
Vector2f transformPoint (float x, float y) const
Transform a 2D point.
Vector2f transformPoint (const Vector2f &point) const
Transform a 2D point.
FloatRect transformRect (const FloatRect &rectangle) const
Transform a rectangle.
Transform & combine (const Transform &transform)
Combine the current transform with another one.
Transform & translate (float x, float y)
Combine the current transform with a translation.
Transform & translate (const Vector2f &offset)
Combine the current transform with a translation.
Transform & rotate (float angle)
Combine the current transform with a rotation. More...
Transform & rotate (float angle, float centerX, float centerY)
Combine the current transform with a rotation. More...
Transform & rotate (float angle, const Vector2f ¢er)
Combine the current transform with a rotation. More...
Transform & scale (float scaleX, float scaleY)
Combine the current transform with a scaling. More...
Transform & scale (float scaleX, float scaleY, float centerX, float centerY)
Combine the current transform with a scaling. More...
Transform & scale (const Vector2f &factors)
Combine the current transform with a scaling. More...
Transform & scale (const Vector2f &factors, const Vector2f ¢er)
Combine the current transform with a scaling. More...
Static Public Attributes
static const Transform Identity
The identity transform (does nothing)
不太能看的懂 这里的sf::Transform。还是看sf::Transformable吧
sf::Tranformable
通过定义一个position,rotation,scale分解的transform。
这个类是为了方便而提供的。在sf::Transform之上。
sf::Transform,作为一个低级类,提供了很大水平的灵活性,但不总是方便管理。
实际上,
Indeed, one can easily combine any kind of operation, such as a translation followed by a rotation followed by a scaling, but once the result transform is built, there's no way to go backward and, let's say, change only the rotation without modifying the translation and scaling.
The entire transform must be recomputed, which means that you need to retrieve the initial translation and scale factors as well, and combine them the same way you did before updating the rotation. This is a tedious冗长的,啰嗦的,单调乏味的 operation, and it requires to store all the individual components of the final transform.
That's exactly what sf::Transformable was written for: it hides these variables and the composed组成 transform behind an easy to use interface. You can set or get any of the individual components without worrying about the others. It also provides the composed transform (as a sf::Transform), and keeps it up-to-date.
In addition to the position, rotation and scale, sf::Transformable provides an "origin" component, which represents the local origin of the three other components. Let's take an example with a 10x10 pixels sprite. By default, the sprite is positioned/rotated/scaled relatively to its top-left corner, because it is the local point (0, 0). But if we change the origin to be (5, 5), the sprite will be positioned/rotated/scaled around its center instead. And if we set the origin to (10, 10), it will be transformed around its bottom-right corner.
To keep the sf::Transformable class simple, there's only one origin for all the components. You cannot position the sprite relatively to its top-left corner while rotating it around its center, for example. To do such things, use sf::Transform directly.
sf::Transformable can be used as a base class. It is often combined with sf::Drawable – that's what SFML's sprites, texts and shapes do.
A note on coordinates and undistorted无失真的 rendering:
By default, SFML (or more exactly, OpenGL) may interpolate drawable objects such as sprites or texts when rendering. While this allows transitions like slow movements or rotations to appear smoothly, it can lead to unwanted results in some cases, for example blurred模糊的 or distorted objects. In order to render a sf::Drawable object pixel-perfectly, make sure the involved coordinates allow a 1:1 mapping of pixels in the window to texels (pixels in the texture). More specifically, this means:
The object's position, origin and scale have no fractional part
The object's and the view's rotation are a multiple of 90 degrees
The view's center and size have no fractional part
1,const Transform & getTransform () const 这是返回引用
get the combined transform of the object
2,剩下的都是setPosition,Rotation,Scale,get这些,还有move,rotate,scale。都是套路的。
但是用法:没有完全理解!
class MyEntity: public sf::Transformable, public sf::Drawable
{
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const
{
states.transform* = getTransform();//这是地址等于,不是乘以等于!错!就是乘以等于,矩阵的合并变换
target.draw(..., states);
}
};
MyEntity entity;
entity.setPosition(10,20);
window.draw(entity);
这里面的用法:transform和transformable属于RenderStates的一种?
就是Transform没理解! Transformable好理解。
Transform和Transformable的关系也是没有理解!
可能需要看opengl?因为就是理解不了transform。
????????????????
???????????????????
??????????????????????
首先Transform和Transformable一定是有关系的。Transform是底层操作。Transformable在Transform之上。
但是不是继承关系!Transformable是组合的Transform的操作?反正就一个getTransform()函数挂钩了这2个类,Transform类根本用不上,最多就是用到Transformable类的getTransform()函数!!
解决!
下面回到 3.5 vertex array中的Texturing,【下午5点半】
【晚上7点】必须放慢脚步学了,大脑头部有些疲劳。现在又不是入睡的时间。
还要工作3个小时。
好像该用了?不然记不住!
【3月31号,周三,上午6点半】复习 3.5使用顶点数组设计自己的实体
看题目它的目标不是vertex array就完事,而是冲着本节最后的大题目!用vertex array加贴图(texture),而不是sprite加贴图。sprite其实也很快,没必要用vertex array!但是还是要学!看看它是什么,vertex array比shape 应该快一丢丢,实际上差不多。但还是要学 自己通过vertex array创建绘制实体 的过程。
1, 通过vertex绘制primitive图形的 第一种方式:定义VertexArray
sf::VertexArray triangle(sf::Triangle,3);
triangle[0].positioon = sf::Vector2f(10.f, 10.f);
…
triangle[2].position = sf::Vector2f(100.f, 100.f);
triangle[0].color = sf::Color::Red;
…
triangle[2].color = sf::Color::Green;
window.draw(triangle);
2,对比不用VertexArray,而用Vertex➕std::vector
std::vector
vertices.push_back(sf::Vertex(…));
…
vertices.push_back(sf::Vertex(sf::Vector2f(10.f,50.f), sf::Color::Red, sf::Vector2f(100.f,100.f));
window.draw(vertices, vertices.size(), sf::Triangles);
下面绘制直线:
sf::Vertex vertices[2]
{
sf::Vertex(…),
sf::Vertex(sf::Vector2f(10,10))
};
window.draw(vertices,2,sf::Lines);
2, Primitive类型
sf::Points
sf::Lines
sf::LineStrip
sf::Triangles
sf::TriangleStrip
sf::TriangleFan
sf::Quads
3, Texturing贴图化
要实现 vertex array的贴图,需要设置vertices的texCoords属性!这个属性定义了texture的哪些像素被映射到vertex。
sf::VertexArray quad(sf::Quads, 4);
quad[0].position = sf::Vector2f(10.f, 10.f);
…
quad[3].position = sf::Vector2f(10.f,110.f);
quad[0].texCoords = sf::Vector2f(0.f,0.f);
…
quad[0].texCoords = sf::Vector2f(0.f,50.f);
Texture coordinates单位是像素,跟sprite和shape的textureRect是一样的。它们不是熟悉opengl的0-1之间的数字。
看来texCoords参数是textureRect的用顶点表达的方式。setTextureRect的表达方式是IntRect(坐标,尺寸)。它是在texture上剪切矩形!
sf::VertexArray vertices;
sf::Texture texture;
…
window.draw(vertices, &texture);
这种是简化的调用方式,隐式地调用了RenderStates的单参数构造函数。
正规的调用方式是:
sf::VertexArray vertices;
sf::Texture texture;
…
sf::RenderStates states;
states.texture = &texture; //其实起名都跟属性重名了。
window.draw(vertices, states);
这里:一是texture用指针不用引用的方式,仅仅在sprite里是用的texture的引用,其它在shape和vertex array里都是用texture的指针!
二是VertexArray的构造函数。
三是 vertex array是必须用的了,不能再像上面用sf::Vertex加std::vector<>加primitive向window的draw函数里传来贴图化了?是的 ,虽然顶点加primitive的方式可以传renderStates,但是texture不能选子矩形了!
所以必须用上VertexArray,因为它有texCoords属性。错了,sf::Vertex有3个属性,顶点坐标、顶点颜色和贴图坐标。所以完全也可以不用VertexArray,而用sf::Vertex。
虽然如此,用vertex array还是比单用Vertex要方便!
4,变换transforming一个vertex array。
跟texturing一样,transform信息不存储在vertex array里,你必须把它通过renderStates传入到draw方法。
renderStates一共4种,transform(或transformale),texturing,shader还有BlendMode。
sf::VertexArray vertices;
sf::Transform transform;//(可以通过getTransform从transformable对象获得,因transform太底层了)
…
window.draw(vertices, tranform);
或者用RenderStates
sf::VertexArray vertices;
sf::Transform transform;
…
RenderStates states;
states.transform = transform;
window.draw(vertices, states);
5,创建于一个SFML-like 实体
大块头开始了!接下来是sfml最最重要的地方了。理解后sfml就只剩下view和shader了。上下的就是前面system和后面 声音!
sf::Drawable是一个接口:它只有一个纯虚函数,没有数据成员没有实函数。继承自sf::Drawable让你能像sfml内置类一样绘制你自己的类对象。
class MyEntity: public sf::Drawable
{
private:
virtual void draw(sf::RenderTarget& target, sf::RenderSrtates states) const;
};
MyEntity entity;
window.draw(entity);
sf::Transformable,没有虚函数。继承自它能让你的类像sfml内置类一样自动添加同样的变换功能。
class MyEntity: pubic sf::Drawable, public sf::Transformable
{
public:
private:
virtural void draw(sf::RenderTarget& target, sf::RenderStates states) const
{
states.transform* = getTransform();
states.texture = &m_texture;
target.draw(m_vertices, states);//这是调用的RenderTarget类方法
}
sf::VertexArray m_vertices;
sf::Texture m_texture;
};
MyEntity entity;
entity.setPosition(10.f, 10.f);
window.draw(entity);
// 看一下RenderWindow和RenderTarget的类方法!
draw方法里为什么要写draw函数?如果没有draw方法?
看一下sprite中是怎样定义的draw函数?
void Sprite::draw(RenderTarget& target, RenderStates states) const
{
if (m_texture)
{
states.transform * = getTransform();
states.texture = m_texture;
target.draw(m_vertices, 4, TriangleStrip, states);
}
}
看来:Drawable类的draw函数的定义方法就是target.draw(),
然后,RenderWindow的draw方法再定义的时候调用它,它里面又会调用RenderWindow的draw,怎么递归成这样?
sprite类里面放 texture原来也是靠这样。
这一关算过了!其实根本用不到自己定义sfml实体。因为连sprite都是用的vertex array。还用自己再定义什么呢?它这道题就是让我们走一遍来理解sprite!
这里面draw函数的具体定义调用了RenderTarget中的draw,
6 实例:tile map 瓦片地图
整个地图将被保存在一个vertex array中,所以它能超级快地被绘制。
我们能应用这个策略,仅仅是在整套瓦片可以放进一个texture中。
不然的话,我们只能每个texture使用一个vertex array了。
class TileMap: public sf::Drawable, public sf::Transformable
{
public:
}
看一下它是怎么处理瓷砖(瓦片)地图碎片的?
第二,它是怎样怎样设计 load方法的?
这个例子真应该走一遍。还要自己重新把tile set往sprite里放。
7,实例:颗粒系统
第二个实例实现另一个通用实体:颗粒系统。这个很简单,没有texture,尽可能少的参数。它阐释了 sf::Points primitive类型使用动态的vertex array,每帧变化。
肯定用到 随机数函数。
竟然还用到了时间。
看一下人家是怎样用cpp和sfml的。但是sf::Points是没有宽度的,都是单像素,不如用sf::Circle?
不学vertex array根本就没有sfml到家。
自己写这个类竟然写不出来!
书上的texture直接拿了一个典型例子,但没有逐级难度,一下子整个类复杂起来了。
texture有没有获取尺寸的方法:
有,getSize()const,返回sf::Vector2u
我先写一个(简单起步的),跟书上对照!
不足够理解sf::Drawable, 也就不理解Drawable里的draw函数的具体定义。为什么要递归调用呢?
它仅是一个接口,并不是必要的要继承自drawable,它只是让调用方式更一致window.draw(object), 而不是object.draw(window)。关键的draw在RenderTarget里!真正的draw在RenderTarget里!
剩下来还有:没有理解draw的调用用的什么技术?
void RenderTarget::draw(const Drawable& drawable, const RenderStates& states)
{
drawable.draw(*this, states);
}
用它就调换过来了双方。
试一下不用继承自drawable!
3.6 位置、旋转、缩放:变换实体
1,变换sfml实体
【下午2点40】中午睡了,最后睡着了,效果就很好。第一个问题继续解决:draw函数的问题。
看源代码和api!首要的是源代码。看Drawable、RenderTarget,还有sprite、RenderWindow,RenderStates。
1,drawable类只是为了统一调用方式。drawable类非常简单,也没有cpp文件,只有hpp文件。
如果没有drawable类,可以在RenderTarget类里面写draw(
draw有一个参数不是drawable的重载,是sf::Vertex* vertices,还有2个是VertexBuffer,最终draw是用vertex,而不是用drawable!
那drawable对象到底是怎样起作用呢?drawable类怎样起作用呢?
drawable的对象有sprite,shape,text,和vertexArray。
一开始学的时候以为 drawable对象才能绘制到RenderTarget上,实际上这是假象,drawable对象其实什么都不是,最终是调用 以vertices为参数的 draw的重载才绘制到RenderTarget上!
sprite,shape,text,vertexArray这些底层都是 vertices!
所以是首先就学错了。
下面验证!
先看drawable类怎样工作!
可以定义自己的实体,继承自Drawable类。但自己的实体最低也得是 Vertex,更低的可能就是vertexbuffer了,还用不到。首先它必须重写Drawable类的纯虚函数。重写的里面必须调用target.draw()。draw的最底下肯定是 vertex,VertexArray也是继承自Drawable,看VertexArray是怎样重写draw纯虚函数的,还有看sprite(它也继承自Drawable)是怎样重写draw纯虚函数的,它们底下都是vertex。
那Drawable到底是怎样工作的?还是没有落实下来。只找到了一个大框架上的真理。
void VertexArray::draw(RenderTarget& target, RenderStates states) const
{
if (!m_vertices.empty())
target.draw(&m_vertices[0], m_vertices.size(), m_primitiveType, states);
}
这是VertexArray重写的draw。调用的是draw的vertices的重载!
void Sprite::draw(RenderTarget& target, RenderStates states) const
{
if (m_texture)
{
states.transform *= getTransform();
states.texture = m_texture;
target.draw(m_vertices, 4, TriangleStrip, states);
}
}
这是sprite的draw的重写,调用的也是vertices的重载。
再看Drawable怎样工作:
比如是常用的数据成员是vertexArray时,draw里写target.draw(vertexarray, states).
用window.draw(entity)调用时,会调用entity.draw(*this, states);
而这个draw里面又会调用 VertexArray的draw的重写,会调用起draw的vertices的重载函数。
Drawable里必须重写draw函数,它必然调用vertices的重载。
那Drawable起了个什么作用?
如果没有Drawable也可以的,它就是转换一下调用方,怎么证明:不证明了,反正都知道了。
不要考虑没有它会怎么样了!
知道结论:底层都是调用的vertex,Drawable对象没有任何实际实体,vertex是顶点,是实际的,RenderTarget是画布,也是实际的。但Drawable没有实际的,它只是交换调用方和参数方。
下面看变换。贴图碎片和颗粒系统很重要很重要,但放到最后看!
3.6 位置、旋转和缩放: 变换实体
1,变换sfml内置实体
sf::Transformable简单易用,覆盖了99%的使用情形,剩下1%看最后几章。
4个属性:position,rotation,scale,origin。
entity.setPosition();
entity.move();
sf::Vector2f position = entity.getPosition();
实体的位置是它的左上角相对于窗口左上角的坐标,实体的原点改变的话是相对于初始的原点。
rotation,当你调用getRotation时 sfml总是返回[0,360)的角度,包括0,不包括360.
scale,缩放因子默认是1,大于1变大,小于1变小。负数是镜像实体。
origin,默认在实体的左上角。只有一个实体原点。
2,变换你自己的类
要找回实体最后一次的变换(绘制时经常用到),调用getTransform函数。这个函数返回sf::Transform对象。
如果你不想要sf::Transformable里面的一整套的方法,你可以把它作为成员在其之上写你自己的函数。不一定非作为基类,可以实例化它。
3,自定义transforms
比如组合几种变换为一个最终变换。使用低级的类sf::Transform。它就是一个3*3的矩阵,所以它可以在2d空间中表示任何变换。
有很多方法可以构造sf::Transform.
1对于最通常的变换(变换,旋转,缩放)通过预定义的方法
2组合两种变换
3直接指定它的9个元素。
sf::Transform t1 = sf::Transform::Identity;
sf::Transform t2;
t2.rotate(45.f);
sf::Transform t3(
2,0,20,
0,1,50,
0,0,1);
sf::Transform t4=t1*t2*t3;
sf::Transform t;
t.translate(10,100);
t.rotate(90);
t.translate(-10,50);
t.scale(0.5,0.75);
window.draw(entity,transform);
sf::RenderStates states;
states.transform = transform;
win.draw(entity,states);
底层的,语法跟Transformable也不一样。
如果是Transformable的对象,内部的和传入的transform会组合产生最后的变换。
这里有对RenderStates的认识。
4,外接矩形
碰撞检测时会非常有用。
sf::FloatRect boundingBox = entity.getGlobalBounds();
sf::Vector2f point = …;
if (boundingBox.contains(point))
{
//碰撞了
}
sf::FloatRect otherBox =…;
if (boundingBox.intersects(otherBox))
//碰撞了
5,对象继承层次(scene graph)场景图,场景树
【简单哲学风格像丢了一样】略过不看。
牵连到sf::Transform这个低级类。因为它牵连矩阵。
3.7 通过shaders添加特殊效果
这一节也用不到。因为要写glsl的代码。
Shaders are written in GLSL (OpenGL Shading Language)
3.8 使用views控制2D镜头
1,什么是view
【2021年4月1日,周四,晚上7点45】新的一个月。今天下午2点半到5点打新冠疫苗,下午6点开始学了一点儿sf::Event。今天晚上像昨天晚上一样写?有点儿效果不行。所以转到电脑上来。
是练习还是继续巩固呢?练习写一点儿,今天晚上的效果不太好,适合写代码。
【4月2号,周五,下午2点20】现在还不适合写项目。要转到windows上吗?不用。
现在仍然应当复习sfml!计划好的话会有一个程度让你觉得适合去写项目了。先写项目,再看书。先看教材不行!一边看着教材,一边写教材上的项目更好一些。不要写自己的项目。练习项目,还是需要写自己的!
事件的应用?
数据类型,数据类型下的变量。
都在忘记啊!
但sfml必须短时间抓上来!因为不然cpp也要忘了。
【下午5点40,实际上现在才有心情学sfml!心情比较好,这很美妙。当然要抓住!sfml到事件了!现在这个时间一下子就容易学了。上午和下午的时候学习还很难进入。吃了鱼香肉丝盖饭和一个火腿尖椒饼。比较饱。】
从sf::Event继续:现在用一下。一个小正方形在(小正方形的做法有1,从rectangle做出来,2,从circle做出来,3,从vertex和quads pirmitive做出来,4,从凸形指定4个顶点做出来。关于椭圆形的做法?自带的还没有,官网教程里有一个自定义。
还是有点累,不是那样精力充沛。
慢下来每种做法做一下倒是可以。慢下来是最正确的做法,但慢下来特别需要大方向!大方向对了才能沉稳地慢慢练习。以前是按照官网tutorials,现在要偏自己设计教程。哪个放到前面哪个放到后面?
在纸笔写个大纲,在这里也要誊写一遍?
要复习的要点 写一写大纲。
内容限在官网Tutorials。去除OpenGL。没有声音和网络。
内容限在官网Tutorials和API文档。必须及时复习!
内容虽然限在这里面,还要用!!
用需要自己出题目。题目能出来将增加很厉害!
还有ide里的项目管理。英文水平也绝对是提不上去的。用中文又容易不支持和乱码。
用英文绝对需要难度,大脑会分出去精力。
这样就提出来开始用中文项目名字。
有很多问题都解决不了,如linux里的中文输入法都多少年了,没有一个人能解决。
chessbase的编码问题也没人能解决。ryzen vega8的黑苹果显卡的问题也解决不了。很多问题都解决不了。有重重障碍。
操作系统要学,汇编语言,cpu原理要学。
所以现在的进度即时很顺利了,也不是什么惊奇!应该让现在的顺利进入常态。应该抓住简单哲学风格。谭的c++书得出来简单好!sfml的作者又让你得出来简单好!越简单越好!
不应该总是那么难!现在怎么又进入难了:没理解!先理解概念!如sf::Event这里。
要练事件,然后理解事件。
飞机大战那个用图形实现!这个项目很好。pygame项目很多个。
节奏!
【2021年4月3日,周六,早上7点半】sfml官网Tutorials全书完成!开用!
第一,正方形的5种画法。第二,使用实时设备轮询(练习一遍)!
第一,正方形的5种画法:
1,sf::Renctangle
2, sf::Circle
3, sf::Quads, primitive
4, sf::ConvexShape
5, what else?
SFML大方向:五大要点,
出书的内容五大要点:1,贴图碎片,2,颗粒系统,3,oop思路,4,椭圆形,5,顶点+贴图
这5大要点能扩展出去很多。包揽了graphics模块的基本原理。都是基础、简单,不易赚钱!在用上赚钱?不分享这些简单的?看你先巩固好这些基础!再看你的用!用好你就能赚钱。
问题一: static成员函数,static 数据成员,c++的知识点。它们是什么意思?就是代替全局函数和全局变量的意思。访问类内非静态成员话用加类名作用域运算符或对象名点号。
clion的图标真难看。但还是用它,而不用xcode。
rotate的方向是 顺时针为正方向。
switch的语法。
下面学一下c++ 随机数函数:
包含
srand(time(nullptr));// 只精确到秒
std::cout << rand() << std::endl;
就能产生0-32767之间的随机整数。
如果要产生0-100之间的随机整数,用rand()%100,包括0,不包括100.
5-100的,用 rand()%(100-5) +5,
要产生小数的话,用rand()&100*0.01
【2021年4月4日,周日,晚上7点40】其实macos这边不如windows的软件好用。但mac这边好看。所以我就来到了mac这边。这边的软件也可以。印象笔记,clion等,都很好。就是不行。这边就不了。
【2021年4月5日,周一,上午8点半】准备图片,图片尺寸怎么办,不用管用sf::View? 这对于非矢量图就不行。图片只能自己制作。要学一下ps吗?mac没有美图秀秀。制作图片gimp和预览够用。
【上午9点半】怎样调整?静不下心来看sf::View。教材马上就到,我该做怎样的安排?
【4月6日,周二,早上6点45】现在的任务,1,查sf::Transformable的源代码是不是下面调用的sf::Transform,2,总结sf::View的笔记。
sf::View,第一,画布是无限大的,画布就是RenderTarget,可以是窗口中的RenderTarget,也可以是Texture中的RenderTarget,用clear()清空时填充的颜色是无限大的。调再大的view也是那么大。view和viewport是一起的,只是默认有一个viewport,默认的viewport是0,0,1,1,它是从窗口左上角铺满整个窗口。先说view,它的坐标的第一种左上角坐标和尺寸的表示方法,比如200,200,300,200,左上角坐标指的是画布向200,200即右下角方向移动的距离,窗口原点是不动的,永远就是窗口左上角是0,0,view设置了200,200后画布向右下角移动了200,200,而尺寸300,200指截取了原来画布300,200的大小,按照默认viewport 铺满窗口。这里关键是坐标需要解释,就是画布是无限大,坐标指向右下角移动的距离。
第二,viewport,0.2,0.2,0.5,1指相对于窗口的尺寸,前面是放置到距离窗口原点0.2,0.2,后面是视口的相对于窗口尺寸显示范围。是这个view以多大比例在窗口的什么位置显示。刚学view时绘制点形状或draw点spirte便于参照看出来。
第三,还有如果要让图片在窗口调整大小的时候不会拉伸怎样做到。
首先图片不让它随窗口尺寸调整而拉伸,这是viewport的事情,viewport前面2个是在窗口的哪里显示,后面2个是占窗口的多大比例(0.5是压窄了背景,而不是截取背景的0.5。需要上机试!对的),最大化窗口后宽伸长了,所以要压缩,压缩比例是窗口高/窗口宽。注意要用float转换一下窗口高的类型,不然整数相除丢失精度。不能用窗口调整显示画布更多内容因为这样图片根本没有放大。也不能用调整view的显示内容区域。就是用viewport。
第四,坐标调整是怎么回事。
【上午8点15】只剩下 坐标转换问题了,然后转向看 sf::Transformable和sf::Transform的底下都是什么。基础就差不多了。
坐标转换:
使用了自定义view后和调整了窗口大小后。RenderTarget上的坐标跟原来的像素坐标就不匹配了。使用mapPixelToCoords,映射像素坐标到RenderTarget坐标系,rendertarget没有绝对原点。
理解了,就是不知道什么时候用到!
下面开始sf::Transform了!
scene graph信息太少,根本不足以看懂!
sf::Transform的三维矩阵是什么?
There are many ways to construct a sf::Transform:
by using the predefined functions for the most common transformations (translation, rotation, scale)
by combining two transforms
by specifying its 9 elements directly
// a custom matrix
sf::Transform t3(2.f, 0.f, 20.f,
0.f, 1.f, 50.f,
0.f, 0.f, 1.f);
// a combined transform
sf::Transform t4 = t1 * t2 * t3;
不足以发布内容到社交 动态,不够低级。
二维(平面)内的平移、旋转、缩放都可以用2x2的矩阵表示出来。用3x3的矩阵表示二维变换是为了 使多个变换可以合并成一个复合矩阵。
具体2x2矩阵怎样表示的3种变换还有3x3矩阵怎样方便合并成复合矩阵需要的知识短时间补不全。
反正知道计算机里是借助矩阵执行图形变换。
这是比OpenGL还底下的事情,不求甚解
所以,sf::Transform不需要了解太多。
sfml的window和graphics模块都可以了。开始用还是再复习一遍再用。有点儿急切地想用。
【下午4点15,浏览复习了一遍,可以开始用sfml了!】用从什么地方开始呢?教材?这样较好还是开始自己找项目?
做sfml教材里的项目,也读。4本教材。
学习的顺序还是读教材时自己先思考,避免先入为主走马观花了!