创意自画像——交互式码绘作品

前言

Processing是一种灵活的软件写生本,是一种学习如何在视觉艺术的环境中编写代码的语言。自2001年以来,Processing促进了视觉艺术中的软件素养和技术中的视觉素养。有成千上万的学生、艺术家、设计师、研究人员和业余爱好者使用加工来学习和制作原型。
在之前对processing有了初步的了解之后,根据老师的要求,开始了交互式的码绘创作。接下来来介绍我这次创作的这幅作品。

成果展示

静态

这次,我绘制的对象是《弹丸论破2》里的女主角——七海千秋
那么,首先来看一下最终实现的效果:


成品

主要的功能如下:
1.背景的苹果会不断生成,并且自转同时往右下角匀速移动。
2.会绘制一个Switch游戏机,会跟随鼠标移动而移动。
3.人物的眼睛会鼠标(游戏机)的移动而转动,并对准该方向。
4.按住鼠标时,人物的眼睛会发光,游戏机屏幕上会显示文字(Nintendo Switch),且背景苹果转动和平移的速度将加快。

那么接下来,我就分块讲解一下这些功能是如何实现的。

人物头像

从编程思路的复杂程度而言,头像这个版块应该是最简单的,但实际上实施起来是非常困难的。因为可以看到,组成人物头像的线条不是规则的,是由很多曲线,直线,填充(在Processing里面并没有想象中那么简单)组成的。废话不多说,接下来就来介绍人物头像的制作。

头发线条

头发线条是真的把我折磨得最惨的一个部分了,能成功画完真的是为爱发电,因为我太喜欢这个角色了。。
简单来说,就是画一大堆贝塞尔曲线。

我是把头发分成了三个组——总的外围轮廓,一些细线和刘海。
拿外围轮廓举例。如果要画一个完整的轮廓线,代码的结构应该如下这样的:

beginShape();
vertex(x0,y0);
bezierVertex(x1,y1);
bezierVertex(x2,y2);
……
endShape();

在beginShape()函数和endShape()函数之间,所有画的曲线会被当做是一个图形,这样方便在beginShape()函数之前修改这个图形的线宽,填充颜色等等。
vertex()函数在这里的作用是作为第一条曲线的起点,如果加在中间,就会自动从上一句曲线的终点连接一条直线到该点,并将该点作为下一条曲线的起点。
bezierVertex()函数有六个参数。分别为第一个控制点的x,y坐标,第二个控制点的x,y坐标,以及终点的x,y坐标。如果连续输入bezierVertex()函数,前一个的终点将作为下一条曲线的起点。
然后。。就慢慢调参数吧。。。
我建议大家可以找一些辅助工具,或者用一些方法更方便获取点的坐标,比如如下代码:

text(mouseX+" "+mouseY,mouseX+20,mouseY-20);

这一句可以实时显示鼠标位置,稍微减少工作量。

颜色填充

正如我之前说的,填充在processing里面可不是意见方便事。如果是封闭图形的填充到还好,就如之前的头发轮廓函数内,只需在beginShape()函数前添加一句:

fill(r,g,b);

头发轮廓的填充后效果如下:


头发轮廓

如果不是封闭图形,processing会自动把最后一个点和第一个点连起来,这有时候会导致填充范围的错误,我就因为这个问题改了很久的代码。。
所以我推荐是,将图片分层,分散到不同函数里去编写,然后搞清他们的遮挡关系,逐一覆盖,这样效果会好很多。
比如,我的脸其实画的很长:


face

但是问题不大,前面会再添加刘海图层,会把多出来的部分覆盖:


加上刘海

最后为了还原人物,我还加了一个不是很还原的高光。。
因为工作量实在太大,就没弄渐变,这一层已经很致命了。。
加上高光

这里高光不需要轮廓,我调用了noStroke()函数,但不要忘记下一步需要轮廓的时候再用stroke()函数修改回来。

鼠标跟随——游戏机

Processing里面鼠标跟随非常方便,直接获取鼠标的x,y坐标——mouseX,mouseY即可,可直接作为参数使用。
这里的游戏机我还是用了分层叠加的方式。

  fill(#00FFFD);
  rect(mouseX-50,mouseY-25,100,50,10);
  fill(#FF4040);
  rect(mouseX-50,mouseY-25,30,50,10);
  fill(0);
  rect(mouseX-30,mouseY-25,60,50);
  fill(#C1C1C1);
  rect(mouseX-24,mouseY-20,48,40);

这里用了一个技巧,rect带第四个参数表示圆角矩形,最后一个参数表示圆滑程度。
可以看到,上述代码是先画了一个蓝色的圆角矩阵,然后再画一个红色的圆角矩阵和左侧对齐,再在中间画一个黑色的方形矩阵,最后再画一个较小的灰色矩阵表示屏幕。
然后是手柄的位置,也可以同理得出坐标,只需要多多尝试,不断调整位置就行。

眼睛的跟随

眼睛的跟随不同于游戏机,眼睛的位置不能超过眼眶的范围,根据我的测试,将眼眶范围定为了一个正方形,然后根据鼠标位置判断,代码如下:

  if(mouseX>449)
  {
    eyeX=449;
  }
  else if(mouseX<437)
  {
    eyeX=437;
  }
  else eyeX=mouseX;
  
   if(mouseY>207)
  {
    eyeY=207;
  }
  else if(mouseY<203)
  {
    eyeY=203;
  }
  else eyeY=mouseY; 
  ellipse(eyeX,eyeY,25,25);
  ellipse(eyeX+70,eyeY,25,25);

这个方法虽然简单,思路清晰,但是也有缺陷,就是当鼠标位于眼眶斜对角的位置(比如左下角)时,无论如何动鼠标,眼睛就不会动。但是这没有办法,因为相当于一个圆卡在了矩形的角落里。如果眼眶的范围是一个圆,那就可以计算出眼睛中心和鼠标的x和y的差值,然后单位化,再赋给眼睛坐标,这样就可以平滑移动了。

背景——苹果的平移和转动

这一部分,我主要讲rotate()函数和translate()函数。
首先声明,processing里面所有的旋转平移等变换都是对坐标轴进行变换。
那么有人会问(比如我),那如果我只想让其中一个物体旋转该怎么办呢?
官方文档给出了解决办法:用 pushMatrix()函数和popMatrix()函数。
再画图前,先把要画的内容压入栈,画完后再出栈,这样就能实现部分物体的变换了。代码结构如下所示:

    pushMatrix();  
    translate(x,y);    
    rotate(angle);
    paint();
    popMatrix();

但是我在画的时候遇到了问题,就是物体无论如何都不动,这可咋整呢?
我去网上参考了别的使用了rotate函数的代码,发现,他们把rotate函数放在draw函数里调用后,参数angle每次都自加0.01,这样一来角度就能不断变化,就可以实现不断旋转的效果了,平移同理。

但是另一个问题又来了——苹果转的时候的旋转中心是坐标原点,如下图所示:


绕原点旋转

这个问题我又去查了查,发现大多数人都是修改了载入模式,把它置为CENTER,可是我去官方文档查找,只有例如rectMode,ellipseMode,imageMode等函数,而我这个苹果是自己画的一个函数。用这些没有效果。。
最后我用了一个投机取巧的办法:我把画完的苹果截图,去掉背景,然后再载入,将imageMode设为CENTER,就可以自转了~

在做完平移和旋转后,只要生成很多个苹果,相隔一定距离,就可以产生成品里面的效果了。

按下鼠标事件

processing中的按下鼠标事件也非常简单,本次我主要是用到了以下两个函数:
mousePressed();
mouseReleased();

mousePressed()函数在按下鼠标的一瞬间会被调用,而mouseReleased()函数则是在松开鼠标的一瞬间会被调用。略加思索,我定义了一个bool变量,控制鼠标是否被按下。然后在mousePressed()里将其置为true,在mouseReleased()函数里将其置为false,代码如下:

void mousePressed()
{
  isPress=true;
}

void mouseReleased()
{
  isPress=false;
}

然后就可以进行一些操作了。。
比如,在背景方面,如果isPress变量为true,则设置速度为两倍;

    if(!isPress)
    {
    angle+=0.01;
    posx+=1;
    posy+=1;
    }
    else
    {
    angle+=0.02;
    posx+=2;
    posy+=2;
    }

在眼睛函数里,如果isPress变量为true,就额外画两个星星(用arc函数)

  if(isPress)
  {
  stroke(#FEFF03);
  strokeWeight(3);
  //fill(#FEFF03);
  noFill();
  arc(eyeX-12.5,eyeY-12.5,25,25,0,HALF_PI);
  arc(eyeX+12.5,eyeY-12.5,25,25,HALF_PI,PI);
  arc(eyeX+12.5,eyeY+12.5,25,25,PI,HALF_PI+PI); 
  arc(eyeX-12.5,eyeY+12.5,25,25,HALF_PI+PI,2*PI);
  
  arc(eyeX+57.5,eyeY-12.5,25,25,0,HALF_PI);
  arc(eyeX+82.5,eyeY-12.5,25,25,HALF_PI,PI);
  arc(eyeX+82.5,eyeY+12.5,25,25,PI,HALF_PI+PI);
  arc(eyeX+57.5,eyeY+12.5,25,25,HALF_PI+PI,2*PI);
  }

在游戏机函数里,如果isPress变量为true,就额外显示一串字符:

if(isPress)
  {
  fill  (0);
  textSize(10);
  text("Nintendo",mouseX-22,mouseY-5);
  text("Switch",mouseX-18,mouseY+5);
  }

就是这么简单,但是这样一来交互就变得更加丰富,多样了。

总结

这次码绘虽然有很多重复而繁琐的工作,工作量很大,但是完成的作品还算美观(自我感觉良好),交互方式多样,有趣,成就感还是蛮大的。也学到了不少知识,对processing的一些函数更为熟悉,对代码中逻辑的思维能力也有所加强,算是不小的收获。

最后,附上这次实验的所有代码:

float angle=0;
int posx=0,posy=0;
PImage img;
boolean isPress=false;

void hairOutline()
{
stroke(3);
fill(#CBBFC8);
beginShape();
strokeWeight(3);
vertex(482,74);
bezierVertex(417,65,373,97,374,184);
bezierVertex(366,203,334,215,326,186);
bezierVertex(331,226,361,217,369,220);
bezierVertex(351,227,331,227,319,208);
bezierVertex(329,229,340,233,380,234);
bezierVertex(346,242,329,232,317,221);
bezierVertex(335,243,356,247,389,241);
bezierVertex(367,260,350,260,334,255);
bezierVertex(361,268,380,270,445,244);
vertex(551,249);
bezierVertex(567,257,585,258,606,246);
vertex(582,241);
bezierVertex(589,242,607,242,624,232);
bezierVertex(607,231,590,228,577,220);
bezierVertex(587,227,604,230,621,215);
bezierVertex(602,219,579,214,569,200);
bezierVertex(589,219,613,216,630,189);
bezierVertex(530,219,628,71,475,73);
endShape();
}

void hairLine()
{
//strokeWeight(3);
noFill();
beginShape();
vertex(420,101);
bezierVertex(361,135,411,225,381,234);
endShape();

beginShape();
vertex(538,111);
bezierVertex(579,139,537,191,580,223);
endShape();
beginShape();
vertex(524,113);
bezierVertex(577,175,529,222,581,241);
endShape();
line(369,220,389,212);
}

void hairThinLine()
{
 //noFill();
strokeWeight(2);
beginShape();
vertex(391,150);
bezierVertex(395,198,349,232,326,186);
endShape();
beginShape();
vertex(389,212);
bezierVertex(376,225,350,240,319,208);
endShape();
beginShape();
vertex(403,216);
bezierVertex(398,236,388,241,389,241);
endShape();
//right
beginShape();
vertex(572,184);
bezierVertex(582,209,609,212,630,189);
endShape();
beginShape();
vertex(560,216);
bezierVertex(571,232,590,247,618,236);
endShape();
}

void face()
{
strokeWeight(3);
fill(255);
beginShape();
vertex(411,144);
bezierVertex(411,212,420,230,432,242);
bezierVertex(440,254,455,263,475,271);
bezierVertex(498,267,508,257,522,244);
bezierVertex(536,209,531,227,542,144);
endShape();

}

void liuhai()
{
  //frontHair left&right
strokeWeight(3);
 fill(#CBBFC8);
beginShape();
vertex(417,117);
bezierVertex(369,198,424,316,480,278);
bezierVertex(429,290,417,234,413,194);
endShape();

fill(#CBBFC8);
beginShape();
vertex(522,113);
bezierVertex(593,190,522,299,496,285);
bezierVertex(501,289,539,261,538,194);
line(538,194,530,186);
bezierVertex(527,156,532,175,532,190);
endShape();
//

fill(#CBBFC8);
beginShape();
vertex(420,129);
bezierVertex(406,164,411,177,416,185);
bezierVertex(420,188,425,185,430,184);
bezierVertex(428,155,427,170,432,139);
endShape();


beginShape();
vertex(432,139);
bezierVertex(428,155,427,170,430,184);
bezierVertex(434,183,441,180,446,180);
bezierVertex(446,172,445,164,448,145);
endShape();


beginShape();
vertex(448,145);
bezierVertex(446,172,445,164,446,182);
bezierVertex(452,185,461,185,467,181);
bezierVertex(465,164,466,149,469,134);
endShape();

beginShape();
vertex(469,134);
bezierVertex(465,164,466,149,467,181);
bezierVertex(474,184,481,187,490,180);
bezierVertex(491,152,490,172,493,128);
endShape();

beginShape();
vertex(493,128);
bezierVertex(493,151,502,168,506,180);
bezierVertex(508,182,515,183,517,184);
bezierVertex(511,171,505,157,505,144);
endShape();

beginShape();
vertex(505,144);
bezierVertex(505,157,511,171,519,185);
bezierVertex(520,186,529,188,538,193);
bezierVertex(529,169,529,143,513,130);
endShape();
}

void hairColor()
{
fill(#E3CEDD);
strokeWeight(0);
//noStroke();
beginShape();
vertex(532,85);
bezierVertex(543,97,548,105,537,112);
bezierVertex(547,120,541,129,538,132);
bezierVertex(542,136,524,169,507,165);
bezierVertex(496,155,493,142,495,128);
bezierVertex(490,150,486,168,476,176);
bezierVertex(469,172,465,173,455,177);
bezierVertex(453,179,448,179,443,179);
bezierVertex(431,167,436,148,440,139);
bezierVertex(429,155,427,166,426,179);
bezierVertex(409,174,423,137,437,123);
bezierVertex(366,179,425,294,469,280);
bezierVertex(382,277,388,133,420,119);
bezierVertex(409,128,407,137,399,143);
bezierVertex(405,101,414,114,422,100);
bezierVertex(390,126,403,114,380,153);
bezierVertex(385,81,462,69,476,75);
bezierVertex(472,72,520,71,532,85);
endShape();
}

void eyesLine()
{
stroke(0);
strokeWeight(5);
noFill();
beginShape();
vertex(423,201);
bezierVertex(430,190,457,188,460,198);
endShape();
beginShape();
vertex(423,204);
bezierVertex(425,210,431,214,435,216);
endShape();
strokeWeight(4);
line(423,201,419,201);
strokeWeight(1);
line(426,196,419,190);
line(429,193,422,189);
//眉毛
beginShape();
vertex(411,180);
bezierVertex(434,177,452,179,465,188);
endShape();
//中心477 202
 strokeWeight(5);
 noFill();
beginShape();
vertex(531,201);
bezierVertex(524,190,497,188,494,198);
endShape();
beginShape();
vertex(531,204);
bezierVertex(529,210,523,214,519,216);
endShape();
strokeWeight(4);
line(531,201,535,201);
strokeWeight(1);
line(528,196,535,190);
line(525,193,532,189);
//眉毛
beginShape();
vertex(543,180);
bezierVertex(520,177,502,179,489,188);
endShape();
}

void mouse_nose()
{
  line(474,220,474,229);
  line(474,229,477,231);
  noStroke();
  fill(#FFE3F7);
  ellipse(510,225,22,11);
  ellipse(444,225,22,11);
  //嘴
  
strokeWeight(0);
fill(#A74A4A);
beginShape();
vertex(462,255);
bezierVertex(461,234,493,235,493,255);
bezierVertex(483,264,469,264,461,254);
endShape();
//舌头
fill(#D36666);
beginShape();
vertex(493,255);
bezierVertex(482,251,470,251,462,255);
bezierVertex(469,264,483,264,493,255);
endShape();

//牙齿
fill(255);
beginShape();
vertex(466,244);
bezierVertex(473,245,481,246,488,243);
bezierVertex(482,239,473,238,466,244);
endShape();
}

void eyeball()
{
  int eyeX,eyeY;
  strokeWeight(1);
  fill(#F0AEC2);
  //if(mouseX>449||mouseX<437 && mouseY>207||mouseY<203)
  
  if(mouseX>449)
  {
    eyeX=449;
  }
  else if(mouseX<437)
  {
    eyeX=437;
  }
  else eyeX=mouseX;
  
   if(mouseY>207)
  {
    eyeY=207;
  }
  else if(mouseY<203)
  {
    eyeY=203;
  }
  else eyeY=mouseY; 
  ellipse(eyeX,eyeY,25,25);
  ellipse(eyeX+70,eyeY,25,25);
  
  if(isPress)
  {
  stroke(#FEFF03);
  strokeWeight(3);
  //fill(#FEFF03);
  noFill();
  arc(eyeX-12.5,eyeY-12.5,25,25,0,HALF_PI);
  arc(eyeX+12.5,eyeY-12.5,25,25,HALF_PI,PI);
  arc(eyeX+12.5,eyeY+12.5,25,25,PI,HALF_PI+PI); 
  arc(eyeX-12.5,eyeY+12.5,25,25,HALF_PI+PI,2*PI);
  
  arc(eyeX+57.5,eyeY-12.5,25,25,0,HALF_PI);
  arc(eyeX+82.5,eyeY-12.5,25,25,HALF_PI,PI);
  arc(eyeX+82.5,eyeY+12.5,25,25,PI,HALF_PI+PI);
  arc(eyeX+57.5,eyeY+12.5,25,25,HALF_PI+PI,2*PI);
  }
  
}
void PSP()
{
  fill(#00FFFD);
  rect(mouseX-50,mouseY-25,100,50,10);
  fill(#FF4040);
  rect(mouseX-50,mouseY-25,30,50,10);
  fill(0);
  rect(mouseX-30,mouseY-25,60,50);
  fill(#C1C1C1);
  rect(mouseX-24,mouseY-20,48,40);
  fill(0);
  ellipse(mouseX-40,mouseY-10,10,10);
  ellipse(mouseX-40,mouseY+5,5,5);
  ellipse(mouseX-40,mouseY+15,5,5);
  ellipse(mouseX-45,mouseY+10,5,5);
  ellipse(mouseX-35,mouseY+10,5,5);
  ellipse(mouseX+40,mouseY+10,10,10);
  ellipse(mouseX+40,mouseY-5,5,5);
  ellipse(mouseX+40,mouseY-15,5,5);
  ellipse(mouseX+45,mouseY-10,5,5);
  ellipse(mouseX+35,mouseY-10,5,5);
  if(isPress)
  {
  fill  (0);
  textSize(10);
  text("Nintendo",mouseX-22,mouseY-5);
  text("Switch",mouseX-18,mouseY+5);
  }
}

void apple()
{

stroke(0);
strokeWeight(3);
fill(#FF3939);
beginShape();
vertex(150,118);
bezierVertex(96,105,125,190,152,177);
bezierVertex(202,183,195,99,159,119);
endShape();
fill(#A0805E);
beginShape();
vertex(159,125);
bezierVertex(168,88,146,83,150,125);
endShape();
fill(#2EFF3A);
beginShape();
vertex(163,107);
bezierVertex(157,94,176,91,186,100);
bezierVertex(175,115,166,111,162,105);
endShape();
noFill();
beginShape();
vertex(182,104);
bezierVertex(174,102,169,103,162,105);
endShape();
beginShape();
vertex(146,123);
bezierVertex(153,125,159,125,164,123);
endShape();

}



void my_background()
{
    if(!isPress)
    {
    angle+=0.01;
    posx+=1;
    posy+=1;
    }
    else
    {
    angle+=0.02;
    posx+=2;
    posy+=2;
    }
for(int i=-10000;i<1000;i+=150)
{
  for(int j=-10000;j<1000;j+=150)
  {
   pushMatrix();  
    translate(posx,posy);    
    translate(i,j+50);
    rotate(angle);
    //apple();
  image(img,0,0);
    popMatrix();
  }
 }
}

void mousePressed()
{
  isPress=true;
}

void mouseReleased()
{
  isPress=false;
}

void setup(){
size(960, 560);
background(#FFD1F2);
img = loadImage("a.png");
imageMode(CENTER);
//hairOutline();
//hairLine();
//hairThinLine();
//face();
//hairColor();
//liuhai();
//eyesLine();
}

void draw()
{  
background  (255);
my_background();
hairOutline();
hairLine();
hairThinLine();
face();
hairColor();
liuhai();
eyeball();
eyesLine();
mouse_nose();
PSP();
//apple();
}

感谢观看~


感谢观看
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353

推荐阅读更多精彩内容